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:
parent
43d81d43fe
commit
740fd1b62f
5 changed files with 2041 additions and 0 deletions
58
include/ipa/raspberrypi.h
Normal file
58
include/ipa/raspberrypi.h
Normal 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__ */
|
3
src/libcamera/pipeline/raspberrypi/meson.build
Normal file
3
src/libcamera/pipeline/raspberrypi/meson.build
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
libcamera_sources += files([
|
||||||
|
'raspberrypi.cpp'
|
||||||
|
])
|
1598
src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
Normal file
1598
src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
Normal file
File diff suppressed because it is too large
Load diff
236
src/libcamera/pipeline/raspberrypi/staggered_ctrl.h
Normal file
236
src/libcamera/pipeline/raspberrypi/staggered_ctrl.h
Normal 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 */
|
146
src/libcamera/pipeline/raspberrypi/vcsm.h
Normal file
146
src/libcamera/pipeline/raspberrypi/vcsm.h
Normal 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 */
|
Loading…
Add table
Add a link
Reference in a new issue