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 */
35static inline ia_css_circbuf_elem_t
36ia_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 */
48static 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 */
59static inline uint32_t
60ia_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 */
71void
72ia_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 */
96void 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 */
107uint32_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 */
124uint32_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 */
174uint32_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 */
188uint32_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 */
203bool 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 */
261static inline uint32_t
262ia_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 */
271static inline ia_css_circbuf_elem_t
272ia_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 */
291static inline void
292ia_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