/* * Copyright (c) 2019, Linaro Limited * Copyright (c) 2024, tinyVision.ai Inc. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #if defined(CONFIG_VIDEO_BUFFER_USE_SHARED_MULTI_HEAP) #include #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 K_HEAP_DEFINE(video_buffer_pool, CONFIG_VIDEO_BUFFER_POOL_SZ_MAX*CONFIG_VIDEO_BUFFER_POOL_NUM_MAX); #define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \ k_heap_aligned_alloc(&video_buffer_pool, 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 mem_block { void *data; }; static struct mem_block video_block[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; struct mem_block *block; int i; /* find available video buffer */ for (i = 0; i < ARRAY_SIZE(video_buf); i++) { if (video_buf[i].buffer == NULL) { vbuf = &video_buf[i]; block = &video_block[i]; break; } } if (vbuf == NULL) { return NULL; } /* Alloc buffer memory */ block->data = VIDEO_COMMON_HEAP_ALLOC(align, size, timeout); if (block->data == NULL) { return NULL; } vbuf->buffer = block->data; 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); } void video_buffer_release(struct video_buffer *vbuf) { struct mem_block *block = NULL; int i; /* vbuf to block */ for (i = 0; i < ARRAY_SIZE(video_block); i++) { if (video_block[i].data == vbuf->buffer) { block = &video_block[i]; break; } } vbuf->buffer = NULL; if (block) { VIDEO_COMMON_FREE(block->data); } } int video_format_caps_index(const struct video_format_cap *fmts, const struct video_format *fmt, size_t *idx) { for (int i = 0; fmts[i].pixelformat != 0; i++) { if (fmts[i].pixelformat == fmt->pixelformat && IN_RANGE(fmt->width, fmts[i].width_min, fmts[i].width_max) && IN_RANGE(fmt->height, fmts[i].height_min, fmts[i].height_max)) { *idx = i; return 0; } } return -ENOENT; } void video_closest_frmival_stepwise(const struct video_frmival_stepwise *stepwise, const struct video_frmival *desired, struct video_frmival *match) { uint64_t min = stepwise->min.numerator; uint64_t max = stepwise->max.numerator; uint64_t step = stepwise->step.numerator; uint64_t goal = desired->numerator; /* Set a common denominator to all values */ min *= stepwise->max.denominator * stepwise->step.denominator * desired->denominator; max *= stepwise->min.denominator * stepwise->step.denominator * desired->denominator; step *= stepwise->min.denominator * stepwise->max.denominator * desired->denominator; goal *= stepwise->min.denominator * stepwise->max.denominator * stepwise->step.denominator; /* Saturate the desired value to the min/max supported */ goal = CLAMP(goal, min, max); /* Compute a numerator and denominator */ match->numerator = min + DIV_ROUND_CLOSEST(goal - min, step) * step; match->denominator = stepwise->min.denominator * stepwise->max.denominator * stepwise->step.denominator * desired->denominator; } void video_closest_frmival(const struct device *dev, enum video_endpoint_id ep, struct video_frmival_enum *match) { uint64_t best_diff_nsec = INT32_MAX; struct video_frmival desired = match->discrete; struct video_frmival_enum fie = {.format = match->format}; __ASSERT(match->type != VIDEO_FRMIVAL_TYPE_STEPWISE, "cannot find range matching the range, only a value matching the range"); while (video_enum_frmival(dev, ep, &fie) == 0) { struct video_frmival tmp = {0}; uint64_t diff_nsec = 0, a, b; switch (fie.type) { case VIDEO_FRMIVAL_TYPE_DISCRETE: tmp = fie.discrete; break; case VIDEO_FRMIVAL_TYPE_STEPWISE: video_closest_frmival_stepwise(&fie.stepwise, &desired, &tmp); break; default: __ASSERT(false, "invalid answer from the queried video device"); } a = video_frmival_nsec(&desired); b = video_frmival_nsec(&tmp); diff_nsec = a > b ? a - b : b - a; if (diff_nsec < best_diff_nsec) { best_diff_nsec = diff_nsec; memcpy(&match->discrete, &tmp, sizeof(tmp)); /* The video_enum_frmival() function will increment fie.index every time. * Compensate for it to get the current index, not the next index. */ match->index = fie.index - 1; } } }