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