blob: 3685fe4d0b34361a2d48f4477ef961eb8b3f3501 [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright Linaro Limited
* SPDX-FileCopyrightText: Copyright tinyVision.ai Inc.
* SPDX-FileCopyrightText: The Zephyr Project Contributors
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/video.h>
#include <zephyr/logging/log.h>
#include <zephyr/video/video.h>
LOG_MODULE_REGISTER(video_buffer, CONFIG_VIDEO_LOG_LEVEL);
#if defined(CONFIG_VIDEO_BUFFER_USE_SHARED_MULTI_HEAP)
#include <zephyr/multi_heap/shared_multi_heap.h>
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
shared_multi_heap_aligned_alloc(CONFIG_VIDEO_BUFFER_SMH_ATTRIBUTE, align, size)
#define VIDEO_COMMON_FREE(block) shared_multi_heap_free(block)
#else
#if !defined(CONFIG_VIDEO_BUFFER_POOL_ZEPHYR_REGION)
#define VIDEO_BUFFER_POOL_REGION_NAME __noinit_named(kheap_buf_video_buffer_pool)
#else
#define VIDEO_BUFFER_POOL_REGION_NAME Z_GENERIC_SECTION(CONFIG_VIDEO_BUFFER_POOL_ZEPHYR_REGION_NAME)
#endif
/*
* The k_heap is manually initialized instead of using directly Z_HEAP_DEFINE_IN_SECT
* since the section might not be yet accessible from the beginning, making it impossible
* to initialize it if done via Z_HEAP_DEFINE_IN_SECT
*/
static char VIDEO_BUFFER_POOL_REGION_NAME __aligned(8)
video_buffer_pool_mem[MAX(CONFIG_VIDEO_BUFFER_POOL_HEAP_SIZE, Z_HEAP_MIN_SIZE)];
static struct k_heap video_buffer_pool;
static bool video_buffer_pool_initialized;
static void *video_buffer_k_heap_aligned_alloc(size_t align, size_t bytes, k_timeout_t timeout)
{
if (!video_buffer_pool_initialized) {
k_heap_init(&video_buffer_pool, video_buffer_pool_mem,
MAX(CONFIG_VIDEO_BUFFER_POOL_HEAP_SIZE, Z_HEAP_MIN_SIZE));
video_buffer_pool_initialized = true;
}
return k_heap_aligned_alloc(&video_buffer_pool, align, bytes, timeout);
}
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
video_buffer_k_heap_aligned_alloc(align, size, timeout)
#define VIDEO_COMMON_FREE(block) k_heap_free(&video_buffer_pool, block)
#endif
static struct video_buffer video_buf[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX];
struct video_buffer *video_buffer_aligned_alloc(size_t size, size_t align, k_timeout_t timeout)
{
struct video_buffer *vbuf = NULL;
/* find available video buffer */
for (uint16_t i = 0; i < ARRAY_SIZE(video_buf); i++) {
if (video_buf[i].buffer == NULL) {
vbuf = &video_buf[i];
vbuf->index = i;
break;
}
}
if (vbuf == NULL) {
return NULL;
}
/* Alloc buffer memory */
vbuf->buffer = VIDEO_COMMON_HEAP_ALLOC(align, size, timeout);
if (vbuf->buffer == NULL) {
return NULL;
}
vbuf->memory = VIDEO_MEMORY_INTERNAL;
vbuf->size = size;
vbuf->bytesused = 0;
return vbuf;
}
struct video_buffer *video_buffer_alloc(size_t size, k_timeout_t timeout)
{
return video_buffer_aligned_alloc(size, sizeof(void *), timeout);
}
int video_buffer_release(struct video_buffer *vbuf)
{
if (vbuf == NULL || vbuf->index >= ARRAY_SIZE(video_buf)) {
LOG_ERR("Invalid buffer index: %u", vbuf->index);
return -EINVAL;
}
if (video_buf[vbuf->index].buffer == NULL) {
LOG_ERR("Buffer %u is already released", vbuf->index);
return -EINVAL;
}
if (video_buf[vbuf->index].memory == VIDEO_MEMORY_INTERNAL) {
VIDEO_COMMON_FREE(video_buf[vbuf->index].buffer);
}
video_buf[vbuf->index].buffer = NULL;
return 0;
}
struct video_buffer *video_import_buffer(uint8_t *mem, size_t sz)
{
uint16_t ind;
if (mem == NULL || sz == 0) {
LOG_ERR("Invalid memory address or size");
return NULL;
}
/* Find the 1st available slot in the video buffer pool */
for (ind = 0; ind < ARRAY_SIZE(video_buf); ind++) {
if (video_buf[ind].buffer == NULL) {
break;
}
}
if (ind == ARRAY_SIZE(video_buf)) {
return NULL;
}
/* Populate the internal buffer */
video_buf[ind].index = ind;
video_buf[ind].size = sz;
video_buf[ind].memory = VIDEO_MEMORY_EXTERNAL;
video_buf[ind].buffer = mem;
video_buf[ind].bytesused = 0;
video_buf[ind].timestamp = 0;
return &video_buf[ind];
}
int video_enqueue(const struct device *dev, struct video_buffer *buf)
{
if (dev == NULL || buf == NULL || buf->index >= CONFIG_VIDEO_BUFFER_POOL_NUM_MAX ||
(buf->type != VIDEO_BUF_TYPE_INPUT && buf->type != VIDEO_BUF_TYPE_OUTPUT)) {
return -EINVAL;
}
video_buf[buf->index].type = buf->type;
return video_driver_enqueue(dev, &video_buf[buf->index]);
}
int video_dequeue(const struct device *dev, struct video_buffer **vbuf,
k_timeout_t timeout)
{
if (dev == NULL || vbuf == NULL) {
return -EINVAL;
}
return video_driver_dequeue(dev, vbuf, timeout);
}
int video_transfer_buffer(const struct device *src, const struct device *sink,
enum video_buf_type src_type, enum video_buf_type sink_type,
k_timeout_t timeout)
{
struct video_buffer *buf = &(struct video_buffer){.type = src_type};
int ret;
ret = video_driver_dequeue(src, &buf, timeout);
if (ret < 0) {
return ret;
}
buf->type = sink_type;
return video_driver_enqueue(sink, buf);
}
int video_set_signal(const struct device *dev, struct k_poll_signal *sig)
{
if (dev == NULL || sig == NULL) {
return -EINVAL;
}
return video_driver_set_signal(dev, sig);
}