[v2,2/5] libcamera: Add support for camera flash devices
diff mbox series

Message ID 20250912-flash_reco-v2-2-d5bb80a2e619@emfend.at
State New
Headers show
Series
  • Support for v4l2 flash devices
Related show

Commit Message

Matthias Fend Sept. 12, 2025, 7:13 a.m. UTC
Add a class to model camera flash devices. Currently, only v4l2 flash
devices are supported. The v4l2 flash devices are implemented similar to
the camera lenses.

Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
---
 include/libcamera/internal/camera_flash.h     |  72 ++++++++
 include/libcamera/internal/camera_sensor.h    |   2 +
 src/libcamera/camera_flash.cpp                | 249 ++++++++++++++++++++++++++
 src/libcamera/meson.build                     |   1 +
 src/libcamera/sensor/camera_sensor_legacy.cpp |  13 ++
 src/libcamera/sensor/camera_sensor_raw.cpp    |  13 ++
 6 files changed, 350 insertions(+)

Patch
diff mbox series

diff --git a/include/libcamera/internal/camera_flash.h b/include/libcamera/internal/camera_flash.h
new file mode 100644
index 0000000000000000000000000000000000000000..70234ca96d98dd88a084e2c6804a66e455d87ef8
--- /dev/null
+++ b/include/libcamera/internal/camera_flash.h
@@ -0,0 +1,72 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, matthias.fend@emfend.at
+ *
+ * Camera flash support
+ */
+#pragma once
+
+#include <memory>
+#include <stdint.h>
+#include <string>
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/controls.h>
+
+namespace libcamera {
+
+class MediaEntity;
+class V4L2Subdevice;
+
+class CameraFlash : protected Loggable
+{
+public:
+	enum class Mode {
+		None,
+		Flash,
+		Torch,
+	};
+
+	enum class StrobeSource {
+		Software,
+		External,
+	};
+
+	explicit CameraFlash(const MediaEntity *entity);
+	~CameraFlash();
+	int init();
+	std::optional<Mode> getMode() const;
+	int setMode(Mode mode);
+	const ControlInfo &getFlashIntensityInfo() const;
+	std::optional<int32_t> getFlashIntensity() const;
+	int setFlashIntensity(int32_t intensity);
+	const ControlInfo &getFlashTimeoutInfo() const;
+	std::optional<int32_t> getFlashTimeout() const;
+	int setFlashTimeout(int32_t timeout_us);
+	std::optional<StrobeSource> getStrobeSource() const;
+	int setStrobeSource(StrobeSource source);
+	int startStrobe();
+	int stopStrobe();
+	const ControlInfo &getTorchIntensityInfo() const;
+	std::optional<int32_t> getTorchIntensity() const;
+	int setTorchIntensity(int32_t intensity);
+
+protected:
+	std::string logPrefix() const override;
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraFlash)
+
+	std::optional<int32_t> getSubdevControl(uint32_t id) const;
+	int setSubdevControl(uint32_t id, int32_t value);
+	int validateDriver();
+
+	const MediaEntity *entity_;
+	std::unique_ptr<V4L2Subdevice> subdev_;
+	std::string model_;
+	const ControlInfoMap *controlInfoMap_ = nullptr;
+};
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
index f6ef4df170d43500bc652762e9a575010a6c1cbd..faeecf244c8d42a43eca52ae04813e0c2f80e516 100644
--- a/include/libcamera/internal/camera_sensor.h
+++ b/include/libcamera/internal/camera_sensor.h
@@ -28,6 +28,7 @@ 
 
 namespace libcamera {
 
+class CameraFlash;
 class CameraLens;
 class MediaEntity;
 class SensorConfiguration;
@@ -48,6 +49,7 @@  public:
 	virtual V4L2Subdevice *device() = 0;
 
 	virtual CameraLens *focusLens() = 0;
+	virtual CameraFlash *flash() = 0;
 
 	virtual const std::vector<unsigned int> &mbusCodes() const = 0;
 	virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
diff --git a/src/libcamera/camera_flash.cpp b/src/libcamera/camera_flash.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d3b9fbf78f063ae0cb67635c4cda667508479534
--- /dev/null
+++ b/src/libcamera/camera_flash.cpp
@@ -0,0 +1,249 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, matthias.fend@emfend.at
+ *
+ * Camera flash support
+ */
+
+#include "libcamera/internal/camera_flash.h"
+
+#include <libcamera/base/utils.h>
+
+#include "libcamera/internal/v4l2_subdevice.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(CameraFlash)
+
+CameraFlash::CameraFlash(const MediaEntity *entity)
+	: entity_(entity)
+{
+}
+
+CameraFlash::~CameraFlash() = default;
+
+int CameraFlash::init()
+{
+	if (entity_->function() != MEDIA_ENT_F_FLASH) {
+		LOG(CameraFlash, Error)
+			<< "Invalid flash function "
+			<< utils::hex(entity_->function());
+		return -EINVAL;
+	}
+
+	subdev_ = std::make_unique<V4L2Subdevice>(entity_);
+	int ret = subdev_->open();
+	if (ret < 0)
+		return ret;
+
+	controlInfoMap_ = &subdev_->controls();
+
+	ret = validateDriver();
+	if (ret)
+		return ret;
+
+	model_ = subdev_->model();
+
+	return 0;
+}
+
+std::optional<CameraFlash::Mode> CameraFlash::getMode() const
+{
+	Mode mode;
+
+	auto v4l2Mode = getSubdevControl(V4L2_CID_FLASH_LED_MODE);
+	if (!v4l2Mode)
+		return std::nullopt;
+
+	switch (*v4l2Mode) {
+	case V4L2_FLASH_LED_MODE_FLASH:
+		mode = Mode::Flash;
+		break;
+	case V4L2_FLASH_LED_MODE_TORCH:
+		mode = Mode::Torch;
+		break;
+	case V4L2_FLASH_LED_MODE_NONE:
+	default:
+		mode = Mode::None;
+		break;
+	}
+
+	return mode;
+}
+
+int CameraFlash::setMode(Mode mode)
+{
+	int32_t v4l2Mode;
+
+	switch (mode) {
+	case Mode::Flash:
+		v4l2Mode = V4L2_FLASH_LED_MODE_FLASH;
+		break;
+	case Mode::Torch:
+		v4l2Mode = V4L2_FLASH_LED_MODE_TORCH;
+		break;
+	case Mode::None:
+		v4l2Mode = V4L2_FLASH_LED_MODE_NONE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return setSubdevControl(V4L2_CID_FLASH_LED_MODE, v4l2Mode);
+}
+
+const ControlInfo &CameraFlash::getFlashIntensityInfo() const
+{
+	return controlInfoMap_->find(V4L2_CID_FLASH_INTENSITY)->second;
+}
+
+std::optional<int32_t> CameraFlash::getFlashIntensity() const
+{
+	return getSubdevControl(V4L2_CID_FLASH_INTENSITY);
+}
+
+int CameraFlash::setFlashIntensity(int32_t intensity)
+{
+	return setSubdevControl(V4L2_CID_FLASH_INTENSITY, intensity);
+}
+
+const ControlInfo &CameraFlash::getFlashTimeoutInfo() const
+{
+	return controlInfoMap_->find(V4L2_CID_FLASH_TIMEOUT)->second;
+}
+
+std::optional<int32_t> CameraFlash::getFlashTimeout() const
+{
+	return getSubdevControl(V4L2_CID_FLASH_TIMEOUT);
+}
+
+int CameraFlash::setFlashTimeout(int32_t timeout)
+{
+	return setSubdevControl(V4L2_CID_FLASH_TIMEOUT, timeout);
+}
+
+std::optional<CameraFlash::StrobeSource> CameraFlash::getStrobeSource() const
+{
+	StrobeSource source;
+
+	auto v4l2Source = getSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE);
+	if (!v4l2Source)
+		return std::nullopt;
+
+	switch (*v4l2Source) {
+	case V4L2_FLASH_STROBE_SOURCE_EXTERNAL:
+		source = StrobeSource::External;
+		break;
+	case V4L2_FLASH_STROBE_SOURCE_SOFTWARE:
+	default:
+		source = StrobeSource::Software;
+		break;
+	}
+
+	return source;
+}
+
+int CameraFlash::setStrobeSource(StrobeSource source)
+{
+	int32_t v4l2Source;
+
+	switch (source) {
+	case StrobeSource::External:
+		v4l2Source = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+		break;
+	case StrobeSource::Software:
+		v4l2Source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return setSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE, v4l2Source);
+}
+
+int CameraFlash::startStrobe()
+{
+	return setSubdevControl(V4L2_CID_FLASH_STROBE, 1);
+}
+
+int CameraFlash::stopStrobe()
+{
+	return setSubdevControl(V4L2_CID_FLASH_STROBE_STOP, 1);
+}
+
+const ControlInfo &CameraFlash::getTorchIntensityInfo() const
+{
+	return controlInfoMap_->find(V4L2_CID_FLASH_TORCH_INTENSITY)->second;
+}
+
+std::optional<int32_t> CameraFlash::getTorchIntensity() const
+{
+	return getSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY);
+}
+
+int CameraFlash::setTorchIntensity(int32_t intensity)
+{
+	return setSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY, intensity);
+}
+
+std::string CameraFlash::logPrefix() const
+{
+	return "'" + entity_->name() + "'";
+}
+
+std::optional<int32_t> CameraFlash::getSubdevControl(uint32_t id) const
+{
+	ControlList controlList = subdev_->getControls(std::array{ id });
+
+	if (controlList.contains(id))
+		return std::nullopt;
+
+	return controlList.get(id).get<int32_t>();
+}
+
+int CameraFlash::setSubdevControl(uint32_t id, int32_t value)
+{
+	ControlList flashCtrls(*controlInfoMap_);
+
+	flashCtrls.set(id, value);
+
+	if (subdev_->setControls(&flashCtrls))
+		return -EINVAL;
+
+	return 0;
+}
+
+int CameraFlash::validateDriver()
+{
+	int ret = 0;
+	static constexpr uint32_t mandatoryControls[] = {
+		V4L2_CID_FLASH_LED_MODE,
+		V4L2_CID_FLASH_STROBE_SOURCE,
+		V4L2_CID_FLASH_STROBE,
+		V4L2_CID_FLASH_TIMEOUT,
+		V4L2_CID_FLASH_INTENSITY,
+		V4L2_CID_FLASH_TORCH_INTENSITY,
+	};
+
+	for (uint32_t ctrl : mandatoryControls) {
+		if (!controlInfoMap_->count(ctrl)) {
+			LOG(CameraFlash, Error)
+				<< "Mandatory V4L2 control " << utils::hex(ctrl)
+				<< " not available";
+			ret = -EINVAL;
+		}
+	}
+
+	if (ret) {
+		LOG(CameraFlash, Error)
+			<< "The flash kernel driver needs to be fixed";
+		LOG(CameraFlash, Error)
+			<< "See Documentation/flash_driver_requirements.rst in"
+			<< " the libcamera sources for more information";
+		return ret;
+	}
+
+	return ret;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index b3ca27f217da4ba3a896ef7cbfb5502fa82a4907..0f125661a51e2431c1febc353cef30a1219f9ce7 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -20,6 +20,7 @@  libcamera_internal_sources = files([
     'bayer_format.cpp',
     'byte_stream_buffer.cpp',
     'camera_controls.cpp',
+    'camera_flash.cpp',
     'camera_lens.cpp',
     'clock_recovery.cpp',
     'control_serializer.cpp',
diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/libcamera/sensor/camera_sensor_legacy.cpp
index f9e685a9acc499fc91d51ed1d66780a0ad2d2a8f..632b66ea0aa15fcd654e7f0efb50c24cb9b973bf 100644
--- a/src/libcamera/sensor/camera_sensor_legacy.cpp
+++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
@@ -31,6 +31,7 @@ 
 #include <libcamera/ipa/core_ipa_interface.h>
 
 #include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera_flash.h"
 #include "libcamera/internal/camera_lens.h"
 #include "libcamera/internal/camera_sensor.h"
 #include "libcamera/internal/camera_sensor_properties.h"
@@ -68,6 +69,7 @@  public:
 	V4L2Subdevice *device() override { return subdev_.get(); }
 
 	CameraLens *focusLens() override { return focusLens_.get(); }
+	CameraFlash *flash() override { return flash_.get(); }
 
 	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
 	std::vector<Size> sizes(unsigned int mbusCode) const override;
@@ -139,6 +141,7 @@  private:
 	ControlList properties_;
 
 	std::unique_ptr<CameraLens> focusLens_;
+	std::unique_ptr<CameraFlash> flash_;
 };
 
 /**
@@ -665,6 +668,16 @@  int CameraSensorLegacy::discoverAncillaryDevices()
 			}
 			break;
 
+		case MEDIA_ENT_F_FLASH:
+			flash_ = std::make_unique<CameraFlash>(ancillary);
+			ret = flash_->init();
+			if (ret) {
+				LOG(CameraSensor, Error)
+					<< "Flash initialisation failed, flash disabled";
+				flash_.reset();
+			}
+			break;
+
 		default:
 			LOG(CameraSensor, Warning)
 				<< "Unsupported ancillary entity function "
diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp
index 8ea4423698cd8c1eaae43eb5ba8b5d524b94d515..9d533d814b9df453aa4009a87818c1558bcbd665 100644
--- a/src/libcamera/sensor/camera_sensor_raw.cpp
+++ b/src/libcamera/sensor/camera_sensor_raw.cpp
@@ -32,6 +32,7 @@ 
 #include <libcamera/ipa/core_ipa_interface.h>
 
 #include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera_flash.h"
 #include "libcamera/internal/camera_lens.h"
 #include "libcamera/internal/camera_sensor.h"
 #include "libcamera/internal/camera_sensor_properties.h"
@@ -69,6 +70,7 @@  public:
 	V4L2Subdevice *device() override { return subdev_.get(); }
 
 	CameraLens *focusLens() override { return focusLens_.get(); }
+	CameraFlash *flash() override { return flash_.get(); }
 
 	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
 	std::vector<Size> sizes(unsigned int mbusCode) const override;
@@ -150,6 +152,7 @@  private:
 	ControlList properties_;
 
 	std::unique_ptr<CameraLens> focusLens_;
+	std::unique_ptr<CameraFlash> flash_;
 };
 
 /**
@@ -513,6 +516,16 @@  std::optional<int> CameraSensorRaw::init()
 			}
 			break;
 
+		case MEDIA_ENT_F_FLASH:
+			flash_ = std::make_unique<CameraFlash>(ancillary);
+			ret = flash_->init();
+			if (ret) {
+				LOG(CameraSensor, Error)
+					<< "Flash initialisation failed, flash disabled";
+				flash_.reset();
+			}
+			break;
+
 		default:
 			LOG(CameraSensor, Warning)
 				<< "Unsupported ancillary entity function "