blob: d9f7c143794dd04ac646b00dd2821338d1ec6266 [file] [log] [blame]
// 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);
}