[libcamera-devel,v3,2/4] libcamera: pipeline: vimc: Switch to using the RGB/YUV Capture video node

Message ID 20190808000404.10841-3-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • libcamera: Fix issues with vimc and Linux v5.2
Related show

Commit Message

Niklas Söderlund Aug. 8, 2019, 12:04 a.m. UTC
Linux commit 85ab1aa1fac17bcd ("media: vimc: deb: fix default sink bayer
format") which is part of v5.2 changes the default media bus format for
the debayer subdevices. This leads to a -EPIPE error when trying to use
the raw capture video device nodes.

Fix this by moving the vimc pipeline to use the RGB/YUV Capture capture
video node. As a consequence of this change the scaler in the vimc
pipeline is used and a hard coded upscale of 3 is present in the video
pipeline. This limits the sizes exposed and accepted by libcamera to
multiples of 3.

The raw capture video node still needs to be handled by the pipeline as
its format needs to be updated to allow the pipeline format validation
to pass.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 src/libcamera/pipeline/vimc.cpp | 112 ++++++++++++++++++++++++++++----
 test/camera/buffer_import.cpp   |   8 +--
 2 files changed, 104 insertions(+), 16 deletions(-)

Comments

Laurent Pinchart Aug. 8, 2019, 7:47 a.m. UTC | #1
Hi Niklas,

Thank you for the patch.

On Thu, Aug 08, 2019 at 02:04:02AM +0200, Niklas Söderlund wrote:
> Linux commit 85ab1aa1fac17bcd ("media: vimc: deb: fix default sink bayer
> format") which is part of v5.2 changes the default media bus format for
> the debayer subdevices. This leads to a -EPIPE error when trying to use
> the raw capture video device nodes.
> 
> Fix this by moving the vimc pipeline to use the RGB/YUV Capture capture
> video node. As a consequence of this change the scaler in the vimc
> pipeline is used and a hard coded upscale of 3 is present in the video
> pipeline. This limits the sizes exposed and accepted by libcamera to
> multiples of 3.

You should also mention that the tests are update according to the new
vimc format requirements.

> The raw capture video node still needs to be handled by the pipeline as
> its format needs to be updated to allow the pipeline format validation
> to pass.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/libcamera/pipeline/vimc.cpp | 112 ++++++++++++++++++++++++++++----
>  test/camera/buffer_import.cpp   |   8 +--
>  2 files changed, 104 insertions(+), 16 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
> index 3d6808222a8a2c5d..55975cae066d71cd 100644
> --- a/src/libcamera/pipeline/vimc.cpp
> +++ b/src/libcamera/pipeline/vimc.cpp
> @@ -10,6 +10,8 @@
>  #include <iomanip>
>  #include <tuple>
>  
> +#include <linux/media-bus-format.h>
> +
>  #include <libcamera/camera.h>
>  #include <libcamera/controls.h>
>  #include <libcamera/ipa/ipa_interface.h>
> @@ -25,6 +27,7 @@
>  #include "pipeline_handler.h"
>  #include "utils.h"
>  #include "v4l2_controls.h"
> +#include "v4l2_subdevice.h"
>  #include "v4l2_videodevice.h"
>  
>  namespace libcamera {
> @@ -35,21 +38,28 @@ class VimcCameraData : public CameraData
>  {
>  public:
>  	VimcCameraData(PipelineHandler *pipe)
> -		: CameraData(pipe), video_(nullptr), sensor_(nullptr)
> +		: CameraData(pipe), sensor_(nullptr), debayer_(nullptr),
> +		  scaler_(nullptr), video_(nullptr), raw_(nullptr)
>  	{
>  	}
>  
>  	~VimcCameraData()
>  	{
>  		delete sensor_;
> +		delete debayer_;
> +		delete scaler_;
>  		delete video_;
> +		delete raw_;
>  	}
>  
>  	int init(MediaDevice *media);
>  	void bufferReady(Buffer *buffer);
>  
> -	V4L2VideoDevice *video_;
>  	CameraSensor *sensor_;
> +	V4L2Subdevice *debayer_;
> +	V4L2Subdevice *scaler_;
> +	V4L2VideoDevice *video_;
> +	V4L2VideoDevice *raw_;
>  	Stream stream_;
>  };
>  
> @@ -80,6 +90,8 @@ public:
>  
>  	int queueRequest(Camera *camera, Request *request) override;
>  
> +	int initLinks(MediaDevice *media);
> +
>  	bool match(DeviceEnumerator *enumerator) override;
>  
>  private:
> @@ -131,8 +143,11 @@ CameraConfiguration::Status VimcCameraConfiguration::validate()
>  	/* Clamp the size based on the device limits. */
>  	const Size size = cfg.size;
>  
> -	cfg.size.width = std::max(16U, std::min(4096U, cfg.size.width));
> -	cfg.size.height = std::max(16U, std::min(2160U, cfg.size.height));
> +	/* Dimensions need to be a multiple of 3 for the scaler to work. */

That's not entirely accurate. It's not that the scaler won't work, but
that the scaler hardcodes a x3 scale up factor. I would write this as

/* The scaler hardcodes a x3 scale-up ratio. */

> +	cfg.size.width = std::max(18U, std::min(4096U, cfg.size.width));
> +	cfg.size.height = std::max(18U, std::min(2160U, cfg.size.height));

18 isn't correct, as the sensor's minimum size is 16x16. The output
minimum size is thus 48x48.

> +	cfg.size.width -= cfg.size.width % 3;
> +	cfg.size.height -= cfg.size.height % 3;
>  
>  	if (cfg.size != size) {
>  		LOG(VIMC, Debug)
> @@ -160,7 +175,7 @@ CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,
>  
>  	StreamConfiguration cfg{};
>  	cfg.pixelFormat = V4L2_PIX_FMT_RGB24;
> -	cfg.size = { 640, 480 };
> +	cfg.size = { 1920, 1080 };
>  	cfg.bufferCount = 4;
>  
>  	config->addConfiguration(cfg);
> @@ -176,6 +191,33 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
>  	StreamConfiguration &cfg = config->at(0);
>  	int ret;
>  
> +	/* The scaler hardcodes a x3 scale-up ratio. */
> +	V4L2SubdeviceFormat subformat = {};
> +	subformat.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8;
> +	subformat.size = { cfg.size.width / 3, cfg.size.height / 3 };
> +
> +	ret = data->sensor_->setFormat(&subformat);
> +	if (ret)
> +		return ret;
> +
> +	ret = data->debayer_->setFormat(0, &subformat);
> +	if (ret)
> +		return ret;
> +
> +	subformat.mbus_code = MEDIA_BUS_FMT_RGB888_1X24;
> +	ret = data->debayer_->setFormat(1, &subformat);
> +	if (ret)
> +		return ret;
> +
> +	ret = data->scaler_->setFormat(0, &subformat);
> +	if (ret)
> +		return ret;
> +
> +	subformat.size = cfg.size;
> +	ret = data->scaler_->setFormat(1, &subformat);
> +	if (ret)
> +		return ret;
> +
>  	V4L2DeviceFormat format = {};
>  	format.fourcc = cfg.pixelFormat;
>  	format.size = cfg.size;
> @@ -188,6 +230,17 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
>  	    format.fourcc != cfg.pixelFormat)
>  		return -EINVAL;
>  
> +	/*
> +	 * Format has to be set on the raw capture video node, otherwise the
> +	 * vimc driver will fail pipeline validation.
> +	 */
> +	format.fourcc = V4L2_PIX_FMT_SGRBG8;
> +	format.size = { cfg.size.width / 3, cfg.size.height / 3 };
> +
> +	ret = data->raw_->setFormat(&format);
> +	if (ret)
> +		return ret;
> +
>  	cfg.setStream(&data->stream_);
>  
>  	return 0;
> @@ -292,6 +345,26 @@ int PipelineHandlerVimc::queueRequest(Camera *camera, Request *request)
>  	return 0;
>  }
>  
> +int PipelineHandlerVimc::initLinks(MediaDevice *media)
> +{
> +	MediaLink *link;
> +	int ret;
> +
> +	ret = media->disableLinks();
> +	if (ret < 0)
> +		return ret;
> +
> +	link = media->link("Debayer B", 1, "Scaler", 0);

You can declare the variable here instead of at the top of the function.

> +	if (!link)
> +		return -ENODEV;
> +
> +	ret = link->setEnabled(true);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
>  bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
>  {
>  	DeviceMatch dm("vimc");
> @@ -318,6 +391,9 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
>  
>  	std::unique_ptr<VimcCameraData> data = utils::make_unique<VimcCameraData>(this);
>  
> +	if (initLinks(media))
> +		return false;
> +

I would move this to the VimcCameraData class to group all
initialisation together. As the amount of code is small you can inline
initLink() in init(), or keep it separate, up to you.

With all these fixed,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  	/* Locate and open the capture video node. */
>  	if (data->init(media))
>  		return false;
> @@ -335,18 +411,30 @@ int VimcCameraData::init(MediaDevice *media)
>  {
>  	int ret;
>  
> -	/* Create and open the video device and the camera sensor. */
> -	video_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1"));
> -	if (video_->open())
> -		return -ENODEV;
> -
> -	video_->bufferReady.connect(this, &VimcCameraData::bufferReady);
> -
> +	/* Create and open the camera sensor, debayer, scaler and video device. */
>  	sensor_ = new CameraSensor(media->getEntityByName("Sensor B"));
>  	ret = sensor_->init();
>  	if (ret)
>  		return ret;
>  
> +	debayer_ = new V4L2Subdevice(media->getEntityByName("Debayer B"));
> +	if (debayer_->open())
> +		return -ENODEV;
> +
> +	scaler_ = new V4L2Subdevice(media->getEntityByName("Scaler"));
> +	if (scaler_->open())
> +		return -ENODEV;
> +
> +	video_ = new V4L2VideoDevice(media->getEntityByName("RGB/YUV Capture"));
> +	if (video_->open())
> +		return -ENODEV;
> +
> +	video_->bufferReady.connect(this, &VimcCameraData::bufferReady);
> +
> +	raw_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1"));
> +	if (raw_->open())
> +		return -ENODEV;
> +
>  	/* Initialise the supported controls. */
>  	const V4L2ControlInfoMap &controls = sensor_->controls();
>  	for (const auto &ctrl : controls) {
> diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp
> index 400d02b350c1aa8f..620ee66dee0570bc 100644
> --- a/test/camera/buffer_import.cpp
> +++ b/test/camera/buffer_import.cpp
> @@ -72,12 +72,12 @@ public:
>  			return ret;
>  		}
>  
> -		format_.size.width = 640;
> -		format_.size.height = 480;
> +		format_.size.width = 1920;
> +		format_.size.height = 1080;
>  		format_.fourcc = V4L2_PIX_FMT_RGB24;
>  		format_.planesCount = 1;
> -		format_.planes[0].size = 640 * 480 * 3;
> -		format_.planes[0].bpl = 640 * 3;
> +		format_.planes[0].size = 1920 * 1080 * 3;
> +		format_.planes[0].bpl = 1920 * 3;
>  
>  		if (video_->setFormat(&format_)) {
>  			cleanup();

Patch

diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
index 3d6808222a8a2c5d..55975cae066d71cd 100644
--- a/src/libcamera/pipeline/vimc.cpp
+++ b/src/libcamera/pipeline/vimc.cpp
@@ -10,6 +10,8 @@ 
 #include <iomanip>
 #include <tuple>
 
+#include <linux/media-bus-format.h>
+
 #include <libcamera/camera.h>
 #include <libcamera/controls.h>
 #include <libcamera/ipa/ipa_interface.h>
@@ -25,6 +27,7 @@ 
 #include "pipeline_handler.h"
 #include "utils.h"
 #include "v4l2_controls.h"
+#include "v4l2_subdevice.h"
 #include "v4l2_videodevice.h"
 
 namespace libcamera {
@@ -35,21 +38,28 @@  class VimcCameraData : public CameraData
 {
 public:
 	VimcCameraData(PipelineHandler *pipe)
-		: CameraData(pipe), video_(nullptr), sensor_(nullptr)
+		: CameraData(pipe), sensor_(nullptr), debayer_(nullptr),
+		  scaler_(nullptr), video_(nullptr), raw_(nullptr)
 	{
 	}
 
 	~VimcCameraData()
 	{
 		delete sensor_;
+		delete debayer_;
+		delete scaler_;
 		delete video_;
+		delete raw_;
 	}
 
 	int init(MediaDevice *media);
 	void bufferReady(Buffer *buffer);
 
-	V4L2VideoDevice *video_;
 	CameraSensor *sensor_;
+	V4L2Subdevice *debayer_;
+	V4L2Subdevice *scaler_;
+	V4L2VideoDevice *video_;
+	V4L2VideoDevice *raw_;
 	Stream stream_;
 };
 
@@ -80,6 +90,8 @@  public:
 
 	int queueRequest(Camera *camera, Request *request) override;
 
+	int initLinks(MediaDevice *media);
+
 	bool match(DeviceEnumerator *enumerator) override;
 
 private:
@@ -131,8 +143,11 @@  CameraConfiguration::Status VimcCameraConfiguration::validate()
 	/* Clamp the size based on the device limits. */
 	const Size size = cfg.size;
 
-	cfg.size.width = std::max(16U, std::min(4096U, cfg.size.width));
-	cfg.size.height = std::max(16U, std::min(2160U, cfg.size.height));
+	/* Dimensions need to be a multiple of 3 for the scaler to work. */
+	cfg.size.width = std::max(18U, std::min(4096U, cfg.size.width));
+	cfg.size.height = std::max(18U, std::min(2160U, cfg.size.height));
+	cfg.size.width -= cfg.size.width % 3;
+	cfg.size.height -= cfg.size.height % 3;
 
 	if (cfg.size != size) {
 		LOG(VIMC, Debug)
@@ -160,7 +175,7 @@  CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,
 
 	StreamConfiguration cfg{};
 	cfg.pixelFormat = V4L2_PIX_FMT_RGB24;
-	cfg.size = { 640, 480 };
+	cfg.size = { 1920, 1080 };
 	cfg.bufferCount = 4;
 
 	config->addConfiguration(cfg);
@@ -176,6 +191,33 @@  int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
 	StreamConfiguration &cfg = config->at(0);
 	int ret;
 
+	/* The scaler hardcodes a x3 scale-up ratio. */
+	V4L2SubdeviceFormat subformat = {};
+	subformat.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8;
+	subformat.size = { cfg.size.width / 3, cfg.size.height / 3 };
+
+	ret = data->sensor_->setFormat(&subformat);
+	if (ret)
+		return ret;
+
+	ret = data->debayer_->setFormat(0, &subformat);
+	if (ret)
+		return ret;
+
+	subformat.mbus_code = MEDIA_BUS_FMT_RGB888_1X24;
+	ret = data->debayer_->setFormat(1, &subformat);
+	if (ret)
+		return ret;
+
+	ret = data->scaler_->setFormat(0, &subformat);
+	if (ret)
+		return ret;
+
+	subformat.size = cfg.size;
+	ret = data->scaler_->setFormat(1, &subformat);
+	if (ret)
+		return ret;
+
 	V4L2DeviceFormat format = {};
 	format.fourcc = cfg.pixelFormat;
 	format.size = cfg.size;
@@ -188,6 +230,17 @@  int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
 	    format.fourcc != cfg.pixelFormat)
 		return -EINVAL;
 
+	/*
+	 * Format has to be set on the raw capture video node, otherwise the
+	 * vimc driver will fail pipeline validation.
+	 */
+	format.fourcc = V4L2_PIX_FMT_SGRBG8;
+	format.size = { cfg.size.width / 3, cfg.size.height / 3 };
+
+	ret = data->raw_->setFormat(&format);
+	if (ret)
+		return ret;
+
 	cfg.setStream(&data->stream_);
 
 	return 0;
@@ -292,6 +345,26 @@  int PipelineHandlerVimc::queueRequest(Camera *camera, Request *request)
 	return 0;
 }
 
+int PipelineHandlerVimc::initLinks(MediaDevice *media)
+{
+	MediaLink *link;
+	int ret;
+
+	ret = media->disableLinks();
+	if (ret < 0)
+		return ret;
+
+	link = media->link("Debayer B", 1, "Scaler", 0);
+	if (!link)
+		return -ENODEV;
+
+	ret = link->setEnabled(true);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
 {
 	DeviceMatch dm("vimc");
@@ -318,6 +391,9 @@  bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
 
 	std::unique_ptr<VimcCameraData> data = utils::make_unique<VimcCameraData>(this);
 
+	if (initLinks(media))
+		return false;
+
 	/* Locate and open the capture video node. */
 	if (data->init(media))
 		return false;
@@ -335,18 +411,30 @@  int VimcCameraData::init(MediaDevice *media)
 {
 	int ret;
 
-	/* Create and open the video device and the camera sensor. */
-	video_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1"));
-	if (video_->open())
-		return -ENODEV;
-
-	video_->bufferReady.connect(this, &VimcCameraData::bufferReady);
-
+	/* Create and open the camera sensor, debayer, scaler and video device. */
 	sensor_ = new CameraSensor(media->getEntityByName("Sensor B"));
 	ret = sensor_->init();
 	if (ret)
 		return ret;
 
+	debayer_ = new V4L2Subdevice(media->getEntityByName("Debayer B"));
+	if (debayer_->open())
+		return -ENODEV;
+
+	scaler_ = new V4L2Subdevice(media->getEntityByName("Scaler"));
+	if (scaler_->open())
+		return -ENODEV;
+
+	video_ = new V4L2VideoDevice(media->getEntityByName("RGB/YUV Capture"));
+	if (video_->open())
+		return -ENODEV;
+
+	video_->bufferReady.connect(this, &VimcCameraData::bufferReady);
+
+	raw_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1"));
+	if (raw_->open())
+		return -ENODEV;
+
 	/* Initialise the supported controls. */
 	const V4L2ControlInfoMap &controls = sensor_->controls();
 	for (const auto &ctrl : controls) {
diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp
index 400d02b350c1aa8f..620ee66dee0570bc 100644
--- a/test/camera/buffer_import.cpp
+++ b/test/camera/buffer_import.cpp
@@ -72,12 +72,12 @@  public:
 			return ret;
 		}
 
-		format_.size.width = 640;
-		format_.size.height = 480;
+		format_.size.width = 1920;
+		format_.size.height = 1080;
 		format_.fourcc = V4L2_PIX_FMT_RGB24;
 		format_.planesCount = 1;
-		format_.planes[0].size = 640 * 480 * 3;
-		format_.planes[0].bpl = 640 * 3;
+		format_.planes[0].size = 1920 * 1080 * 3;
+		format_.planes[0].bpl = 1920 * 3;
 
 		if (video_->setFormat(&format_)) {
 			cleanup();