summaryrefslogtreecommitdiff
path: root/test/serialization/generated_serializer/include
AgeCommit message (Collapse)Author
2022-10-18test: generated_serializer: Test Flags that is struct memberPaul Elder
Add fields to the test struct to test serialization/deserialization of scoped enums and flags that are struct members. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
2022-10-18test: generated_serializer: Test enum that is struct memberPaul Elder
Add an enum field to the test struct member to test serialization/deserialization of enums that are struct members. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-05-10meson: Use new project_*_root() functionsTomi Valkeinen
meson.source_root() and meson.build_root() are deprecated. Use meson.project_source_root() and meson.project_build_root() instead. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2021-03-02tests: Test IPA serializer generationPaul Elder
Add a test to confirm that serializer and header generation works properly for mojom definition files, and that the serializer works properly. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2023, Linaro Ltd
 *
 * Simple software ISP implementation
 */

#include "libcamera/internal/software_isp/software_isp.h"

#include <cmath>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

#include <libcamera/formats.h>
#include <libcamera/stream.h>

#include "libcamera/internal/bayer_format.h"
#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/ipa_manager.h"
#include "libcamera/internal/mapped_framebuffer.h"
#include "libcamera/internal/software_isp/debayer_params.h"

#include "debayer_cpu.h"

/**
 * \file software_isp.cpp
 * \brief Simple software ISP implementation
 */

namespace libcamera {

LOG_DEFINE_CATEGORY(SoftwareIsp)

/**
 * \class SoftwareIsp
 * \brief Class for the Software ISP
 */

/**
 * \var SoftwareIsp::inputBufferReady
 * \brief A signal emitted when the input frame buffer completes
 */

/**
 * \var SoftwareIsp::outputBufferReady
 * \brief A signal emitted when the output frame buffer completes
 */

/**
 * \var SoftwareIsp::ispStatsReady
 * \brief A signal emitted when the statistics for IPA are ready
 */

/**
 * \var SoftwareIsp::setSensorControls
 * \brief A signal emitted when the values to write to the sensor controls are
 * ready
 */

/**
 * \brief Constructs SoftwareIsp object
 * \param[in] pipe The pipeline handler in use
 * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline
 * handler
 */
SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
	: dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
		   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
		   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
{
	/*
	 * debayerParams_ must be initialized because the initial value is used for
	 * the first two frames, i.e. until stats processing starts providing its
	 * own parameters.
	 *
	 * \todo This should be handled in the same place as the related
	 * operations, in the IPA module.
	 */
	std::array<uint8_t, 256> gammaTable;
	for (unsigned int i = 0; i < 256; i++)
		gammaTable[i] = UINT8_MAX * std::pow(i / 256.0, 0.5);
	for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
		debayerParams_.red[i] = gammaTable[i];
		debayerParams_.green[i] = gammaTable[i];
		debayerParams_.blue[i] = gammaTable[i];
	}

	if (!dmaHeap_.isValid()) {
		LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
		return;
	}

	sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
	if (!sharedParams_) {
		LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
		return;
	}

	auto stats = std::make_unique<SwStatsCpu>();
	if (!stats->isValid()) {
		LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
		return;
	}
	stats->statsReady.connect(this, &SoftwareIsp::statsReady);

	debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
	debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
	debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);

	ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
	if (!ipa_) {
		LOG(SoftwareIsp, Error)
			<< "Creating IPA for software ISP failed";
		debayer_.reset();
		return;
	}

	/*
	 * The API tuning file is made from the sensor name. If the tuning file
	 * isn't found, fall back to the 'uncalibrated' file.
	 */
	std::string ipaTuningFile =
		ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");

	int ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
			     debayer_->getStatsFD(),
			     sharedParams_.fd(),
			     sensor->controls());
	if (ret) {
		LOG(SoftwareIsp, Error) << "IPA init failed";
		debayer_.reset();
		return;
	}

	ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams);
	ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);

	debayer_->moveToThread(&ispWorkerThread_);
}

SoftwareIsp::~SoftwareIsp()
{
	/* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
	debayer_.reset();
}

/**
 * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
 * \brief Load a configuration from a file
 * \param[in] filename The file to load the configuration data from
 *
 * Currently is a stub doing nothing and always returning "success".
 *
 * \return 0 on success
 */

/**
 * \brief Process the statistics gathered
 * \param[in] sensorControls The sensor controls
 *
 * Requests the IPA to calculate new parameters for ISP and new control
 * values for the sensor.
 */
void SoftwareIsp::processStats(const ControlList &sensorControls)
{
	ASSERT(ipa_);
	ipa_->processStats(sensorControls);
}

/**
 * \brief Check the validity of Software Isp object
 * \return True if Software Isp is valid, false otherwise
 */
bool SoftwareIsp::isValid() const
{
	return !!debayer_;
}

/**
  * \brief Get the output formats supported for the given input format
  * \param[in] inputFormat The input format
  * \return All the supported output formats or an empty vector if there are none
  */
std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
{
	ASSERT(debayer_);

	return debayer_->formats(inputFormat);
}

/**
 * \brief Get the supported output sizes for the given input format and size
 * \param[in] inputFormat The input format
 * \param[in] inputSize The input frame size
 * \return The valid size range or an empty range if there are none
 */
SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
{
	ASSERT(debayer_);

	return debayer_->sizes(inputFormat, inputSize);
}

/**
 * Get the output stride and the frame size in bytes for the given output format and size
 * \param[in] outputFormat The output format
 * \param[in] size The output size (width and height in pixels)
 * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
 * if there is no valid output config
 */
std::tuple<unsigned int, unsigned int>
SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
{
	ASSERT(debayer_);

	return debayer_->strideAndFrameSize(outputFormat, size);
}

/**
 * \brief Configure the SoftwareIsp object according to the passed in parameters
 * \param[in] inputCfg The input configuration
 * \param[in] outputCfgs The output configurations
 * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor
 * \return 0 on success, a negative errno on failure
 */
int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
			   const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
			   const ControlInfoMap &sensorControls)
{
	ASSERT(ipa_ && debayer_);

	int ret = ipa_->configure(sensorControls);
	if (ret < 0)
		return ret;

	return debayer_->configure(inputCfg, outputCfgs);
}

/**
 * \brief Export the buffers from the Software ISP
 * \param[in] stream Output stream exporting the buffers
 * \param[in] count Number of buffers to allocate
 * \param[out] buffers Vector to store the allocated buffers
 * \return The number of allocated buffers on success or a negative error code
 * otherwise
 */
int SoftwareIsp::exportBuffers(const Stream *stream, unsigned int count,
			       std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
	ASSERT(debayer_ != nullptr);

	/* single output for now */
	if (stream == nullptr)
		return -EINVAL;

	for (unsigned int i = 0; i < count; i++) {
		const std::string name = "frame-" + std::to_string(i);
		const size_t frameSize = debayer_->frameSize();

		FrameBuffer::Plane outPlane;
		outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
		if (!outPlane.fd.isValid()) {
			LOG(SoftwareIsp, Error)
				<< "failed to allocate a dma_buf";
			return -ENOMEM;
		}
		outPlane.offset = 0;
		outPlane.length = frameSize;

		std::vector<FrameBuffer::Plane> planes{ outPlane };
		buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
	}

	return count;
}

/**
 * \brief Queue buffers to Software ISP
 * \param[in] input The input framebuffer
 * \param[in] outputs The container holding the output stream pointers and
 * their respective frame buffer outputs
 * \return 0 on success, a negative errno on failure
 */
int SoftwareIsp::queueBuffers(FrameBuffer *input,
			      const std::map<const Stream *, FrameBuffer *> &outputs)
{
	/*
	 * Validate the outputs as a sanity check: at least one output is
	 * required, all outputs must reference a valid stream.
	 */
	if (outputs.empty())
		return -EINVAL;

	for (auto [stream, buffer] : outputs) {
		if (!buffer)
			return -EINVAL;
		if (outputs.size() != 1) /* only single stream atm */
			return -EINVAL;
	}

	for (auto iter = outputs.begin(); iter != outputs.end(); iter++)
		process(input, iter->second);

	return 0;
}

/**
 * \brief Starts the Software ISP streaming operation
 * \return 0 on success, any other value indicates an error
 */
int SoftwareIsp::start()
{
	int ret = ipa_->start();
	if (ret)
		return ret;

	ispWorkerThread_.start();
	return 0;
}

/**
 * \brief Stops the Software ISP streaming operation
 */
void SoftwareIsp::stop()
{
	ispWorkerThread_.exit();
	ispWorkerThread_.wait();

	ipa_->stop();
}

/**
 * \brief Passes the input framebuffer to the ISP worker to process
 * \param[in] input The input framebuffer
 * \param[out] output The framebuffer to write the processed frame to
 */
void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output)
{
	debayer_->invokeMethod(&DebayerCpu::process,
			       ConnectionTypeQueued, input, output, debayerParams_);
}

void SoftwareIsp::saveIspParams()
{
	debayerParams_ = *sharedParams_;
}

void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
{
	setSensorControls.emit(sensorControls);
}

void SoftwareIsp::statsReady()
{
	ispStatsReady.emit();
}

void SoftwareIsp::inputReady(FrameBuffer *input)
{
	inputBufferReady.emit(input);
}

void SoftwareIsp::outputReady(FrameBuffer *output)
{
	outputBufferReady.emit(output);
}

} /* namespace libcamera */