1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Support for Intel Camera Imaging ISP subsystem.
4 * Copyright (c) 2015, Intel Corporation.
5 */
6
7 #include "ia_css_circbuf.h"
8
9 #include <assert_support.h>
10
11 /**********************************************************************
12 *
13 * Forward declarations.
14 *
15 **********************************************************************/
16 /*
17 * @brief Read the oldest element from the circular buffer.
18 * Read the oldest element WITHOUT checking whether the
19 * circular buffer is empty or not. The oldest element is
20 * also removed out from the circular buffer.
21 *
22 * @param cb The pointer to the circular buffer.
23 *
24 * @return the oldest element.
25 */
26 static inline ia_css_circbuf_elem_t
27 ia_css_circbuf_read(ia_css_circbuf_t *cb);
28
29 /*
30 * @brief Shift a chunk of elements in the circular buffer.
31 * A chunk of elements (i.e. the ones from the "start" position
32 * to the "chunk_src" position) are shifted in the circular buffer,
33 * along the direction of new elements coming.
34 *
35 * @param cb The pointer to the circular buffer.
36 * @param chunk_src The position at which the first element in the chunk is.
37 * @param chunk_dest The position to which the first element in the chunk would be shift.
38 */
39 static inline void ia_css_circbuf_shift_chunk(ia_css_circbuf_t *cb,
40 u32 chunk_src,
41 uint32_t chunk_dest);
42
43 /*
44 * @brief Get the "val" field in the element.
45 *
46 * @param elem The pointer to the element.
47 *
48 * @return the "val" field.
49 */
50 static inline uint32_t
51 ia_css_circbuf_elem_get_val(ia_css_circbuf_elem_t *elem);
52
53 /**********************************************************************
54 *
55 * Non-inline functions.
56 *
57 **********************************************************************/
58 /*
59 * @brief Create the circular buffer.
60 * Refer to "ia_css_circbuf.h" for details.
61 */
62 void
ia_css_circbuf_create(ia_css_circbuf_t * cb,ia_css_circbuf_elem_t * elems,ia_css_circbuf_desc_t * desc)63 ia_css_circbuf_create(ia_css_circbuf_t *cb,
64 ia_css_circbuf_elem_t *elems,
65 ia_css_circbuf_desc_t *desc)
66 {
67 u32 i;
68
69 OP___assert(desc);
70
71 cb->desc = desc;
72 /* Initialize to defaults */
73 cb->desc->start = 0;
74 cb->desc->end = 0;
75 cb->desc->step = 0;
76
77 for (i = 0; i < cb->desc->size; i++)
78 ia_css_circbuf_elem_init(&elems[i]);
79
80 cb->elems = elems;
81 }
82
83 /*
84 * @brief Destroy the circular buffer.
85 * Refer to "ia_css_circbuf.h" for details.
86 */
ia_css_circbuf_destroy(ia_css_circbuf_t * cb)87 void ia_css_circbuf_destroy(ia_css_circbuf_t *cb)
88 {
89 cb->desc = NULL;
90
91 cb->elems = NULL;
92 }
93
94 /*
95 * @brief Pop a value out of the circular buffer.
96 * Refer to "ia_css_circbuf.h" for details.
97 */
ia_css_circbuf_pop(ia_css_circbuf_t * cb)98 uint32_t ia_css_circbuf_pop(ia_css_circbuf_t *cb)
99 {
100 u32 ret;
101 ia_css_circbuf_elem_t elem;
102
103 assert(!ia_css_circbuf_is_empty(cb));
104
105 /* read an element from the buffer */
106 elem = ia_css_circbuf_read(cb);
107 ret = ia_css_circbuf_elem_get_val(&elem);
108 return ret;
109 }
110
111 /*
112 * @brief Extract a value out of the circular buffer.
113 * Refer to "ia_css_circbuf.h" for details.
114 */
ia_css_circbuf_extract(ia_css_circbuf_t * cb,int offset)115 uint32_t ia_css_circbuf_extract(ia_css_circbuf_t *cb, int offset)
116 {
117 int max_offset;
118 u32 val;
119 u32 pos;
120 u32 src_pos;
121 u32 dest_pos;
122
123 /* get the maximum offset */
124 max_offset = ia_css_circbuf_get_offset(cb, cb->desc->start, cb->desc->end);
125 max_offset--;
126
127 /*
128 * Step 1: When the target element is at the "start" position.
129 */
130 if (offset == 0) {
131 val = ia_css_circbuf_pop(cb);
132 return val;
133 }
134
135 /*
136 * Step 2: When the target element is out of the range.
137 */
138 if (offset > max_offset) {
139 val = 0;
140 return val;
141 }
142
143 /*
144 * Step 3: When the target element is between the "start" and
145 * "end" position.
146 */
147 /* get the position of the target element */
148 pos = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start, offset);
149
150 /* get the value from the target element */
151 val = ia_css_circbuf_elem_get_val(&cb->elems[pos]);
152
153 /* shift the elements */
154 src_pos = ia_css_circbuf_get_pos_at_offset(cb, pos, -1);
155 dest_pos = pos;
156 ia_css_circbuf_shift_chunk(cb, src_pos, dest_pos);
157
158 return val;
159 }
160
161 /*
162 * @brief Peek an element from the circular buffer.
163 * Refer to "ia_css_circbuf.h" for details.
164 */
ia_css_circbuf_peek(ia_css_circbuf_t * cb,int offset)165 uint32_t ia_css_circbuf_peek(ia_css_circbuf_t *cb, int offset)
166 {
167 int pos;
168
169 pos = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->end, offset);
170
171 /* get the value at the position */
172 return cb->elems[pos].val;
173 }
174
175 /*
176 * @brief Get the value of an element from the circular buffer.
177 * Refer to "ia_css_circbuf.h" for details.
178 */
ia_css_circbuf_peek_from_start(ia_css_circbuf_t * cb,int offset)179 uint32_t ia_css_circbuf_peek_from_start(ia_css_circbuf_t *cb, int offset)
180 {
181 int pos;
182
183 pos = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start, offset);
184
185 /* get the value at the position */
186 return cb->elems[pos].val;
187 }
188
189 /* @brief increase size of a circular buffer.
190 * Use 'CAUTION' before using this function. This was added to
191 * support / fix issue with increasing size for tagger only
192 * Please refer to "ia_css_circbuf.h" for details.
193 */
ia_css_circbuf_increase_size(ia_css_circbuf_t * cb,unsigned int sz_delta,ia_css_circbuf_elem_t * elems)194 bool ia_css_circbuf_increase_size(
195 ia_css_circbuf_t *cb,
196 unsigned int sz_delta,
197 ia_css_circbuf_elem_t *elems)
198 {
199 u8 curr_size;
200 u8 curr_end;
201 unsigned int i;
202
203 if (!cb || sz_delta == 0)
204 return false;
205
206 curr_size = cb->desc->size;
207 curr_end = cb->desc->end;
208 /* We assume cb was pre defined as global to allow
209 * increase in size */
210 /* FM: are we sure this cannot cause size to become too big? */
211 if (((uint8_t)(cb->desc->size + (uint8_t)sz_delta) > cb->desc->size) &&
212 ((uint8_t)sz_delta == sz_delta))
213 cb->desc->size += (uint8_t)sz_delta;
214 else
215 return false; /* overflow in size */
216
217 /* If elems are passed update them else we assume its been taken
218 * care before calling this function */
219 if (elems) {
220 /* cb element array size will not be increased dynamically,
221 * but pointers to new elements can be added at the end
222 * of existing pre defined cb element array of
223 * size >= new size if not already added */
224 for (i = curr_size; i < cb->desc->size; i++)
225 cb->elems[i] = elems[i - curr_size];
226 }
227 /* Fix Start / End */
228 if (curr_end < cb->desc->start) {
229 if (curr_end == 0) {
230 /* Easily fix End */
231 cb->desc->end = curr_size;
232 } else {
233 /* Move elements and fix Start*/
234 ia_css_circbuf_shift_chunk(cb,
235 curr_size - 1,
236 curr_size + sz_delta - 1);
237 }
238 }
239
240 return true;
241 }
242
243 /****************************************************************
244 *
245 * Inline functions.
246 *
247 ****************************************************************/
248 /*
249 * @brief Get the "val" field in the element.
250 * Refer to "Forward declarations" for details.
251 */
252 static inline uint32_t
ia_css_circbuf_elem_get_val(ia_css_circbuf_elem_t * elem)253 ia_css_circbuf_elem_get_val(ia_css_circbuf_elem_t *elem)
254 {
255 return elem->val;
256 }
257
258 /*
259 * @brief Read the oldest element from the circular buffer.
260 * Refer to "Forward declarations" for details.
261 */
262 static inline ia_css_circbuf_elem_t
ia_css_circbuf_read(ia_css_circbuf_t * cb)263 ia_css_circbuf_read(ia_css_circbuf_t *cb)
264 {
265 ia_css_circbuf_elem_t elem;
266
267 /* get the element from the target position */
268 elem = cb->elems[cb->desc->start];
269
270 /* clear the target position */
271 ia_css_circbuf_elem_init(&cb->elems[cb->desc->start]);
272
273 /* adjust the "start" position */
274 cb->desc->start = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start, 1);
275 return elem;
276 }
277
278 /*
279 * @brief Shift a chunk of elements in the circular buffer.
280 * Refer to "Forward declarations" for details.
281 */
282 static inline void
ia_css_circbuf_shift_chunk(ia_css_circbuf_t * cb,u32 chunk_src,uint32_t chunk_dest)283 ia_css_circbuf_shift_chunk(ia_css_circbuf_t *cb,
284 u32 chunk_src, uint32_t chunk_dest)
285 {
286 int chunk_offset;
287 int chunk_sz;
288 int i;
289
290 /* get the chunk offset and size */
291 chunk_offset = ia_css_circbuf_get_offset(cb,
292 chunk_src, chunk_dest);
293 chunk_sz = ia_css_circbuf_get_offset(cb, cb->desc->start, chunk_src) + 1;
294
295 /* shift each element to its terminal position */
296 for (i = 0; i < chunk_sz; i++) {
297 /* copy the element from the source to the destination */
298 ia_css_circbuf_elem_cpy(&cb->elems[chunk_src],
299 &cb->elems[chunk_dest]);
300
301 /* clear the source position */
302 ia_css_circbuf_elem_init(&cb->elems[chunk_src]);
303
304 /* adjust the source/terminal positions */
305 chunk_src = ia_css_circbuf_get_pos_at_offset(cb, chunk_src, -1);
306 chunk_dest = ia_css_circbuf_get_pos_at_offset(cb, chunk_dest, -1);
307 }
308
309 /* adjust the index "start" */
310 cb->desc->start = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->start,
311 chunk_offset);
312 }
313