// SPDX-License-Identifier: GPL-2.0
|
/*
|
* Support for Intel Camera Imaging ISP subsystem.
|
* Copyright (c) 2015, Intel Corporation.
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms and conditions of the GNU General Public License,
|
* version 2, as published by the Free Software Foundation.
|
*
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* more details.
|
*/
|
|
#include "ia_css_circbuf.h"
|
|
#include <assert_support.h>
|
|
/**********************************************************************
|
*
|
* Forward declarations.
|
*
|
**********************************************************************/
|
/*
|
* @brief Read the oldest element from the circular buffer.
|
* Read the oldest element WITHOUT checking whehter the
|
* circular buffer is empty or not. The oldest element is
|
* also removed out from the circular buffer.
|
*
|
* @param cb The pointer to the circular buffer.
|
*
|
* @return the oldest element.
|
*/
|
static inline ia_css_circbuf_elem_t
|
ia_css_circbuf_read(ia_css_circbuf_t *cb);
|
|
/*
|
* @brief Shift a chunk of elements in the circular buffer.
|
* A chunk of elements (i.e. the ones from the "start" position
|
* to the "chunk_src" position) are shifted in the circular buffer,
|
* along the direction of new elements coming.
|
*
|
* @param cb The pointer to the circular buffer.
|
* @param chunk_src The position at which the first element in the chunk is.
|
* @param chunk_dest The position to which the first element in the chunk would be shift.
|
*/
|
static inline void ia_css_circbuf_shift_chunk(ia_css_circbuf_t *cb,
|
u32 chunk_src,
|
uint32_t chunk_dest);
|
|
/*
|
* @brief Get the "val" field in the element.
|
*
|
* @param elem The pointer to the element.
|
*
|
* @return the "val" field.
|
*/
|
static inline uint32_t
|
ia_css_circbuf_elem_get_val(ia_css_circbuf_elem_t *elem);
|
|
/**********************************************************************
|
*
|
* Non-inline functions.
|
*
|
**********************************************************************/
|
/*
|
* @brief Create the circular buffer.
|
* Refer to "ia_css_circbuf.h" for details.
|
*/
|
void
|
ia_css_circbuf_create(ia_css_circbuf_t *cb,
|
ia_css_circbuf_elem_t *elems,
|
ia_css_circbuf_desc_t *desc)
|
{
|
u32 i;
|
|
OP___assert(desc);
|
|
cb->desc = desc;
|
/* Initialize to defaults */
|
cb->desc->start = 0;
|
cb->desc->end = 0;
|
cb->desc->step = 0;
|
|
for (i = 0; i < cb->desc->size; i++)
|
ia_css_circbuf_elem_init(&elems[i]);
|
|
cb->elems = elems;
|
}
|
|
/*
|
* @brief Destroy the circular buffer.
|
* Refer to "ia_css_circbuf.h" for details.
|
*/
|
void ia_css_circbuf_destroy(ia_css_circbuf_t *cb)
|
{
|
cb->desc = NULL;
|
|
cb->elems = NULL;
|
}
|
|
/*
|
* @brief Pop a value out of the circular buffer.
|
* Refer to "ia_css_circbuf.h" for details.
|
*/
|
uint32_t ia_css_circbuf_pop(ia_css_circbuf_t *cb)
|
{
|
u32 ret;
|
ia_css_circbuf_elem_t elem;
|
|
assert(!ia_css_circbuf_is_empty(cb));
|
|
/* read an element from the buffer */
|
elem = ia_css_circbuf_read(cb);
|
ret = ia_css_circbuf_elem_get_val(&elem);
|
return ret;
|
}
|
|
/*
|
* @brief Extract a value out of the circular buffer.
|
* Refer to "ia_css_circbuf.h" for details.
|
*/
|
uint32_t ia_css_circbuf_extract(ia_css_circbuf_t *cb, int offset)
|
{
|
int max_offset;
|
u32 val;
|
u32 pos;
|
u32 src_pos;
|
u32 dest_pos;
|
|
/* get the maximum offest */
|
max_offset = ia_css_circbuf_get_offset(cb, cb->desc->start, cb->desc->end);
|
max_offset--;
|
|
/*
|
* Step 1: When the target element is at the "start" position.
|
*/
|
if (offset == 0) {
|
val = ia_css_circbuf_pop(cb);
|
return val;
|
}
|
|
/*
|
* Step 2: When the target element is out of the range.
|
*/
|
if (offset > max_offset) {
|
val = 0;
|
return val;
|
}
|
|
/*
|
* Step 3: When the target element is between the "start" and
|
* "end" position.
|
*/
|
/* get the position of the target element */
|
pos = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start, offset);
|
|
/* get the value from the target element */
|
val = ia_css_circbuf_elem_get_val(&cb->elems[pos]);
|
|
/* shift the elements */
|
src_pos = ia_css_circbuf_get_pos_at_offset(cb, pos, -1);
|
dest_pos = pos;
|
ia_css_circbuf_shift_chunk(cb, src_pos, dest_pos);
|
|
return val;
|
}
|
|
/*
|
* @brief Peek an element from the circular buffer.
|
* Refer to "ia_css_circbuf.h" for details.
|
*/
|
uint32_t ia_css_circbuf_peek(ia_css_circbuf_t *cb, int offset)
|
{
|
int pos;
|
|
pos = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->end, offset);
|
|
/* get the value at the position */
|
return cb->elems[pos].val;
|
}
|
|
/*
|
* @brief Get the value of an element from the circular buffer.
|
* Refer to "ia_css_circbuf.h" for details.
|
*/
|
uint32_t ia_css_circbuf_peek_from_start(ia_css_circbuf_t *cb, int offset)
|
{
|
int pos;
|
|
pos = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start, offset);
|
|
/* get the value at the position */
|
return cb->elems[pos].val;
|
}
|
|
/* @brief increase size of a circular buffer.
|
* Use 'CAUTION' before using this function. This was added to
|
* support / fix issue with increasing size for tagger only
|
* Please refer to "ia_css_circbuf.h" for details.
|
*/
|
bool ia_css_circbuf_increase_size(
|
ia_css_circbuf_t *cb,
|
unsigned int sz_delta,
|
ia_css_circbuf_elem_t *elems)
|
{
|
u8 curr_size;
|
u8 curr_end;
|
unsigned int i = 0;
|
|
if (!cb || sz_delta == 0)
|
return false;
|
|
curr_size = cb->desc->size;
|
curr_end = cb->desc->end;
|
/* We assume cb was pre defined as global to allow
|
* increase in size */
|
/* FM: are we sure this cannot cause size to become too big? */
|
if (((uint8_t)(cb->desc->size + (uint8_t)sz_delta) > cb->desc->size) &&
|
((uint8_t)sz_delta == sz_delta))
|
cb->desc->size += (uint8_t)sz_delta;
|
else
|
return false; /* overflow in size */
|
|
/* If elems are passed update them else we assume its been taken
|
* care before calling this function */
|
if (elems) {
|
/* cb element array size will not be increased dynamically,
|
* but pointers to new elements can be added at the end
|
* of existing pre defined cb element array of
|
* size >= new size if not already added */
|
for (i = curr_size; i < cb->desc->size; i++)
|
cb->elems[i] = elems[i - curr_size];
|
}
|
/* Fix Start / End */
|
if (curr_end < cb->desc->start) {
|
if (curr_end == 0) {
|
/* Easily fix End */
|
cb->desc->end = curr_size;
|
} else {
|
/* Move elements and fix Start*/
|
ia_css_circbuf_shift_chunk(cb,
|
curr_size - 1,
|
curr_size + sz_delta - 1);
|
}
|
}
|
|
return true;
|
}
|
|
/****************************************************************
|
*
|
* Inline functions.
|
*
|
****************************************************************/
|
/*
|
* @brief Get the "val" field in the element.
|
* Refer to "Forward declarations" for details.
|
*/
|
static inline uint32_t
|
ia_css_circbuf_elem_get_val(ia_css_circbuf_elem_t *elem)
|
{
|
return elem->val;
|
}
|
|
/*
|
* @brief Read the oldest element from the circular buffer.
|
* Refer to "Forward declarations" for details.
|
*/
|
static inline ia_css_circbuf_elem_t
|
ia_css_circbuf_read(ia_css_circbuf_t *cb)
|
{
|
ia_css_circbuf_elem_t elem;
|
|
/* get the element from the target position */
|
elem = cb->elems[cb->desc->start];
|
|
/* clear the target position */
|
ia_css_circbuf_elem_init(&cb->elems[cb->desc->start]);
|
|
/* adjust the "start" position */
|
cb->desc->start = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start, 1);
|
return elem;
|
}
|
|
/*
|
* @brief Shift a chunk of elements in the circular buffer.
|
* Refer to "Forward declarations" for details.
|
*/
|
static inline void
|
ia_css_circbuf_shift_chunk(ia_css_circbuf_t *cb,
|
u32 chunk_src, uint32_t chunk_dest)
|
{
|
int chunk_offset;
|
int chunk_sz;
|
int i;
|
|
/* get the chunk offset and size */
|
chunk_offset = ia_css_circbuf_get_offset(cb,
|
chunk_src, chunk_dest);
|
chunk_sz = ia_css_circbuf_get_offset(cb, cb->desc->start, chunk_src) + 1;
|
|
/* shift each element to its terminal position */
|
for (i = 0; i < chunk_sz; i++) {
|
/* copy the element from the source to the destination */
|
ia_css_circbuf_elem_cpy(&cb->elems[chunk_src],
|
&cb->elems[chunk_dest]);
|
|
/* clear the source position */
|
ia_css_circbuf_elem_init(&cb->elems[chunk_src]);
|
|
/* adjust the source/terminal positions */
|
chunk_src = ia_css_circbuf_get_pos_at_offset(cb, chunk_src, -1);
|
chunk_dest = ia_css_circbuf_get_pos_at_offset(cb, chunk_dest, -1);
|
}
|
|
/* adjust the index "start" */
|
cb->desc->start = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start,
|
chunk_offset);
|
}
|