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