libcamera: pipeline: Raspberry Pi pipeline handler

Initial implementation of the Raspberry Pi (BCM2835) ISP pipeline
handler.

All code is licensed under the BSD-2-Clause terms.
Copyright (c) 2019-2020 Raspberry Pi Trading Ltd.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
Naushir Patuck 2020-05-03 16:24:05 +01:00 committed by Laurent Pinchart
parent 43d81d43fe
commit 740fd1b62f
5 changed files with 2041 additions and 0 deletions

58
include/ipa/raspberrypi.h Normal file
View file

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019-2020, Raspberry Pi Ltd.
*
* raspberrypi.h - Image Processing Algorithm interface for Raspberry Pi
*/
#ifndef __LIBCAMERA_IPA_INTERFACE_RASPBERRYPI_H__
#define __LIBCAMERA_IPA_INTERFACE_RASPBERRYPI_H__
#include <libcamera/control_ids.h>
#include <libcamera/controls.h>
enum RPiOperations {
RPI_IPA_ACTION_V4L2_SET_STAGGERED = 1,
RPI_IPA_ACTION_V4L2_SET_ISP,
RPI_IPA_ACTION_STATS_METADATA_COMPLETE,
RPI_IPA_ACTION_RUN_ISP,
RPI_IPA_ACTION_RUN_ISP_AND_DROP_FRAME,
RPI_IPA_ACTION_SET_SENSOR_CONFIG,
RPI_IPA_ACTION_EMBEDDED_COMPLETE,
RPI_IPA_EVENT_SIGNAL_STAT_READY,
RPI_IPA_EVENT_SIGNAL_ISP_PREPARE,
RPI_IPA_EVENT_QUEUE_REQUEST,
RPI_IPA_EVENT_LS_TABLE_ALLOCATION,
};
enum RPiIpaMask {
ID = 0x0ffff,
STATS = 0x10000,
EMBEDDED_DATA = 0x20000,
BAYER_DATA = 0x40000
};
/* Size of the LS grid allocation. */
#define MAX_LS_GRID_SIZE (32 << 10)
namespace libcamera {
/* List of controls handled by the Raspberry Pi IPA */
static const ControlInfoMap RPiControls = {
{ &controls::AeEnable, ControlInfo(false, true) },
{ &controls::ExposureTime, ControlInfo(0, 999999) },
{ &controls::AnalogueGain, ControlInfo(1.0f, 32.0f) },
{ &controls::AeMeteringMode, ControlInfo(0, static_cast<int32_t>(controls::MeteringModeMax)) },
{ &controls::AeConstraintMode, ControlInfo(0, static_cast<int32_t>(controls::ConstraintModeMax)) },
{ &controls::AeExposureMode, ControlInfo(0, static_cast<int32_t>(controls::ExposureModeMax)) },
{ &controls::ExposureValue, ControlInfo(0.0f, 16.0f) },
{ &controls::AwbEnable, ControlInfo(false, true) },
{ &controls::ColourGains, ControlInfo(0.0f, 32.0f) },
{ &controls::AwbMode, ControlInfo(0, static_cast<int32_t>(controls::AwbModeMax)) },
{ &controls::Brightness, ControlInfo(-1.0f, 1.0f) },
{ &controls::Contrast, ControlInfo(0.0f, 32.0f) },
{ &controls::Saturation, ControlInfo(0.0f, 32.0f) },
};
} /* namespace libcamera */
#endif /* __LIBCAMERA_IPA_INTERFACE_RASPBERRYPI_H__ */

View file

@ -0,0 +1,3 @@
libcamera_sources += files([
'raspberrypi.cpp'
])

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2020, Raspberry Pi (Trading) Ltd.
*
* staggered_ctrl.h - Helper for writing staggered ctrls to a V4L2 device.
*/
#pragma once
#include <algorithm>
#include <initializer_list>
#include <mutex>
#include <unordered_map>
#include <libcamera/controls.h>
#include "log.h"
#include "utils.h"
#include "v4l2_videodevice.h"
/* For logging... */
using libcamera::LogCategory;
using libcamera::LogDebug;
using libcamera::LogInfo;
using libcamera::utils::hex;
LOG_DEFINE_CATEGORY(RPI_S_W);
namespace RPi {
class StaggeredCtrl
{
public:
StaggeredCtrl()
: init_(false), setCount_(0), getCount_(0), maxDelay_(0)
{
}
~StaggeredCtrl()
{
}
operator bool() const
{
return init_;
}
void init(libcamera::V4L2VideoDevice *dev,
std::initializer_list<std::pair<const uint32_t, uint8_t>> delayList)
{
std::lock_guard<std::mutex> lock(lock_);
dev_ = dev;
delay_ = delayList;
ctrl_.clear();
/* Find the largest delay across all controls. */
maxDelay_ = 0;
for (auto const &p : delay_) {
LOG(RPI_S_W, Info) << "Init ctrl "
<< hex(p.first) << " with delay "
<< static_cast<int>(p.second);
maxDelay_ = std::max(maxDelay_, p.second);
}
init_ = true;
}
void reset()
{
std::lock_guard<std::mutex> lock(lock_);
int lastSetCount = std::max<int>(0, setCount_ - 1);
std::unordered_map<uint32_t, int32_t> lastVal;
/* Reset the counters. */
setCount_ = getCount_ = 0;
/* Look for the last set values. */
for (auto const &c : ctrl_)
lastVal[c.first] = c.second[lastSetCount].value;
/* Apply the last set values as the next to be applied. */
ctrl_.clear();
for (auto &c : lastVal)
ctrl_[c.first][setCount_] = CtrlInfo(c.second);
}
bool set(uint32_t ctrl, int32_t value)
{
std::lock_guard<std::mutex> lock(lock_);
/* Can we find this ctrl as one that is registered? */
if (delay_.find(ctrl) == delay_.end())
return false;
ctrl_[ctrl][setCount_].value = value;
ctrl_[ctrl][setCount_].updated = true;
return true;
}
bool set(std::initializer_list<std::pair<const uint32_t, int32_t>> ctrlList)
{
std::lock_guard<std::mutex> lock(lock_);
for (auto const &p : ctrlList) {
/* Can we find this ctrl? */
if (delay_.find(p.first) == delay_.end())
return false;
ctrl_[p.first][setCount_] = CtrlInfo(p.second);
}
return true;
}
bool set(libcamera::ControlList &controls)
{
std::lock_guard<std::mutex> lock(lock_);
for (auto const &p : controls) {
/* Can we find this ctrl? */
if (delay_.find(p.first) == delay_.end())
return false;
ctrl_[p.first][setCount_] = CtrlInfo(p.second.get<int32_t>());
LOG(RPI_S_W, Debug) << "Setting ctrl "
<< hex(p.first) << " to "
<< ctrl_[p.first][setCount_].value
<< " at index "
<< setCount_;
}
return true;
}
int write()
{
std::lock_guard<std::mutex> lock(lock_);
libcamera::ControlList controls(dev_->controls());
for (auto &p : ctrl_) {
int delayDiff = maxDelay_ - delay_[p.first];
int index = std::max<int>(0, setCount_ - delayDiff);
if (p.second[index].updated) {
/* We need to write this value out. */
controls.set(p.first, p.second[index].value);
p.second[index].updated = false;
LOG(RPI_S_W, Debug) << "Writing ctrl "
<< hex(p.first) << " to "
<< p.second[index].value
<< " at index "
<< index;
}
}
nextFrame();
return dev_->setControls(&controls);
}
void get(std::unordered_map<uint32_t, int32_t> &ctrl, uint8_t offset = 0)
{
std::lock_guard<std::mutex> lock(lock_);
/* Account for the offset to reset the getCounter. */
getCount_ += offset + 1;
ctrl.clear();
for (auto &p : ctrl_) {
int index = std::max<int>(0, getCount_ - maxDelay_);
ctrl[p.first] = p.second[index].value;
LOG(RPI_S_W, Debug) << "Getting ctrl "
<< hex(p.first) << " to "
<< p.second[index].value
<< " at index "
<< index;
}
}
private:
void nextFrame()
{
/* Advance the control history to the next frame */
int prevCount = setCount_;
setCount_++;
LOG(RPI_S_W, Debug) << "Next frame, set index is " << setCount_;
for (auto &p : ctrl_) {
p.second[setCount_].value = p.second[prevCount].value;
p.second[setCount_].updated = false;
}
}
/* listSize must be a power of 2. */
static constexpr int listSize = (1 << 4);
struct CtrlInfo {
CtrlInfo()
: value(0), updated(false)
{
}
CtrlInfo(int32_t value_)
: value(value_), updated(true)
{
}
int32_t value;
bool updated;
};
class CircularArray : public std::array<CtrlInfo, listSize>
{
public:
CtrlInfo &operator[](int index)
{
return std::array<CtrlInfo, listSize>::operator[](index & (listSize - 1));
}
const CtrlInfo &operator[](int index) const
{
return std::array<CtrlInfo, listSize>::operator[](index & (listSize - 1));
}
};
bool init_;
uint32_t setCount_;
uint32_t getCount_;
uint8_t maxDelay_;
libcamera::V4L2VideoDevice *dev_;
std::unordered_map<uint32_t, uint8_t> delay_;
std::unordered_map<uint32_t, CircularArray> ctrl_;
std::mutex lock_;
};
} /* namespace RPi */

View file

@ -0,0 +1,146 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2019, Raspberry Pi (Trading) Limited
*
* vcsm.h - Helper class for vcsm allocations.
*/
#pragma once
#include <iostream>
#include <mutex>
#include <fcntl.h>
#include <linux/vc_sm_cma_ioctl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
namespace RPi {
#define VCSM_CMA_DEVICE_NAME "/dev/vcsm-cma"
class Vcsm
{
public:
Vcsm()
{
vcsmHandle_ = ::open(VCSM_CMA_DEVICE_NAME, O_RDWR, 0);
if (vcsmHandle_ == -1) {
std::cerr << "Could not open vcsm device: "
<< VCSM_CMA_DEVICE_NAME;
}
}
~Vcsm()
{
/* Free all existing allocations. */
auto it = allocMap_.begin();
while (it != allocMap_.end())
it = remove(it->first);
if (vcsmHandle_)
::close(vcsmHandle_);
}
void *alloc(const char *name, unsigned int size,
vc_sm_cma_cache_e cache = VC_SM_CMA_CACHE_NONE)
{
unsigned int pageSize = getpagesize();
void *user_ptr;
int ret;
if (!name)
return nullptr;
/* Ask for page aligned allocation. */
size = (size + pageSize - 1) & ~(pageSize - 1);
struct vc_sm_cma_ioctl_alloc alloc;
memset(&alloc, 0, sizeof(alloc));
alloc.size = size;
alloc.num = 1;
alloc.cached = cache;
alloc.handle = 0;
memcpy(alloc.name, name, 32);
ret = ::ioctl(vcsmHandle_, VC_SM_CMA_IOCTL_MEM_ALLOC, &alloc);
if (ret < 0 || alloc.handle < 0) {
std::cerr << "vcsm allocation failure for "
<< name << std::endl;
return nullptr;
}
/* Map the buffer into user space. */
user_ptr = ::mmap(0, alloc.size, PROT_READ | PROT_WRITE,
MAP_SHARED, alloc.handle, 0);
if (user_ptr == MAP_FAILED) {
std::cerr << "vcsm mmap failure for " << name << std::endl;
::close(alloc.handle);
return nullptr;
}
std::lock_guard<std::mutex> lock(lock_);
allocMap_.emplace(user_ptr, AllocInfo(alloc.handle,
alloc.size, alloc.vc_handle));
return user_ptr;
}
void free(void *user_ptr)
{
std::lock_guard<std::mutex> lock(lock_);
remove(user_ptr);
}
unsigned int getVCHandle(void *user_ptr)
{
std::lock_guard<std::mutex> lock(lock_);
auto it = allocMap_.find(user_ptr);
if (it != allocMap_.end())
return it->second.vcHandle;
return 0;
}
private:
struct AllocInfo {
AllocInfo(int handle_, int size_, int vcHandle_)
: handle(handle_), size(size_), vcHandle(vcHandle_)
{
}
int handle;
int size;
uint32_t vcHandle;
};
/* Map of all allocations that have been requested. */
using AllocMap = std::map<void *, AllocInfo>;
AllocMap::iterator remove(void *user_ptr)
{
auto it = allocMap_.find(user_ptr);
if (it != allocMap_.end()) {
int handle = it->second.handle;
int size = it->second.size;
::munmap(user_ptr, size);
::close(handle);
/*
* Remove the allocation from the map. This returns
* an iterator to the next element.
*/
it = allocMap_.erase(it);
}
/* Returns an iterator to the next element. */
return it;
}
AllocMap allocMap_;
int vcsmHandle_;
std::mutex lock_;
};
} /* namespace RPi */