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