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_queue.h"
17#include <math_support.h>
18#include <ia_css_circbuf.h>
19#include <ia_css_circbuf_desc.h>
20#include "queue_access.h"
21
22/*****************************************************************************
23 * Queue Public APIs
24 *****************************************************************************/
25int ia_css_queue_local_init(ia_css_queue_t *qhandle, ia_css_queue_local_t *desc)
26{
27	if (NULL == qhandle || NULL == desc
28	    || NULL == desc->cb_elems || NULL == desc->cb_desc) {
29		/* Invalid parameters, return error*/
30		return -EINVAL;
31	}
32
33	/* Mark the queue as Local */
34	qhandle->type = IA_CSS_QUEUE_TYPE_LOCAL;
35
36	/* Create a local circular buffer queue*/
37	ia_css_circbuf_create(&qhandle->desc.cb_local,
38			      desc->cb_elems,
39			      desc->cb_desc);
40
41	return 0;
42}
43
44int ia_css_queue_remote_init(ia_css_queue_t *qhandle, ia_css_queue_remote_t *desc)
45{
46	if (NULL == qhandle || NULL == desc) {
47		/* Invalid parameters, return error*/
48		return -EINVAL;
49	}
50
51	/* Mark the queue as remote*/
52	qhandle->type = IA_CSS_QUEUE_TYPE_REMOTE;
53
54	/* Copy over the local queue descriptor*/
55	qhandle->location = desc->location;
56	qhandle->proc_id = desc->proc_id;
57	qhandle->desc.remote.cb_desc_addr = desc->cb_desc_addr;
58	qhandle->desc.remote.cb_elems_addr = desc->cb_elems_addr;
59
60	/* If queue is remote, we let the local processor
61	 * do its init, before using it. This is just to get us
62	 * started, we can remove this restriction as we go ahead
63	 */
64
65	return 0;
66}
67
68int ia_css_queue_uninit(ia_css_queue_t *qhandle)
69{
70	if (!qhandle)
71		return -EINVAL;
72
73	/* Load the required queue object */
74	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
75		/* Local queues are created. Destroy it*/
76		ia_css_circbuf_destroy(&qhandle->desc.cb_local);
77	}
78
79	return 0;
80}
81
82int ia_css_queue_enqueue(ia_css_queue_t *qhandle, uint32_t item)
83{
84	int error;
85
86	if (!qhandle)
87		return -EINVAL;
88
89	/* 1. Load the required queue object */
90	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
91		/* Directly de-ref the object and
92		 * operate on the queue
93		 */
94		if (ia_css_circbuf_is_full(&qhandle->desc.cb_local)) {
95			/* Cannot push the element. Return*/
96			return -ENOBUFS;
97		}
98
99		/* Push the element*/
100		ia_css_circbuf_push(&qhandle->desc.cb_local, item);
101	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
102		ia_css_circbuf_desc_t cb_desc;
103		ia_css_circbuf_elem_t cb_elem;
104		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
105
106		/* a. Load the queue cb_desc from remote */
107		QUEUE_CB_DESC_INIT(&cb_desc);
108		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
109		if (error != 0)
110			return error;
111
112		/* b. Operate on the queue */
113		if (ia_css_circbuf_desc_is_full(&cb_desc))
114			return -ENOBUFS;
115
116		cb_elem.val = item;
117
118		error = ia_css_queue_item_store(qhandle, cb_desc.end, &cb_elem);
119		if (error != 0)
120			return error;
121
122		cb_desc.end = (cb_desc.end + 1) % cb_desc.size;
123
124		/* c. Store the queue object */
125		/* Set only fields requiring update with
126		 * valid value. Avoids unnecessary calls
127		 * to load/store functions
128		 */
129		ignore_desc_flags = QUEUE_IGNORE_SIZE_START_STEP_FLAGS;
130
131		error = ia_css_queue_store(qhandle, &cb_desc, ignore_desc_flags);
132		if (error != 0)
133			return error;
134	}
135
136	return 0;
137}
138
139int ia_css_queue_dequeue(ia_css_queue_t *qhandle, uint32_t *item)
140{
141	int error;
142
143	if (!qhandle || NULL == item)
144		return -EINVAL;
145
146	/* 1. Load the required queue object */
147	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
148		/* Directly de-ref the object and
149		 * operate on the queue
150		 */
151		if (ia_css_circbuf_is_empty(&qhandle->desc.cb_local)) {
152			/* Nothing to pop. Return empty queue*/
153			return -ENODATA;
154		}
155
156		*item = ia_css_circbuf_pop(&qhandle->desc.cb_local);
157	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
158		/* a. Load the queue from remote */
159		ia_css_circbuf_desc_t cb_desc;
160		ia_css_circbuf_elem_t cb_elem;
161		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
162
163		QUEUE_CB_DESC_INIT(&cb_desc);
164
165		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
166		if (error != 0)
167			return error;
168
169		/* b. Operate on the queue */
170		if (ia_css_circbuf_desc_is_empty(&cb_desc))
171			return -ENODATA;
172
173		error = ia_css_queue_item_load(qhandle, cb_desc.start, &cb_elem);
174		if (error != 0)
175			return error;
176
177		*item = cb_elem.val;
178
179		cb_desc.start = OP_std_modadd(cb_desc.start, 1, cb_desc.size);
180
181		/* c. Store the queue object */
182		/* Set only fields requiring update with
183		 * valid value. Avoids unnecessary calls
184		 * to load/store functions
185		 */
186		ignore_desc_flags = QUEUE_IGNORE_SIZE_END_STEP_FLAGS;
187		error = ia_css_queue_store(qhandle, &cb_desc, ignore_desc_flags);
188		if (error != 0)
189			return error;
190	}
191	return 0;
192}
193
194int ia_css_queue_is_full(ia_css_queue_t *qhandle, bool *is_full)
195{
196	int error;
197
198	if ((!qhandle) || (!is_full))
199		return -EINVAL;
200
201	/* 1. Load the required queue object */
202	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
203		/* Directly de-ref the object and
204		 * operate on the queue
205		 */
206		*is_full = ia_css_circbuf_is_full(&qhandle->desc.cb_local);
207		return 0;
208	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
209		/* a. Load the queue from remote */
210		ia_css_circbuf_desc_t cb_desc;
211		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
212
213		QUEUE_CB_DESC_INIT(&cb_desc);
214		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
215		if (error != 0)
216			return error;
217
218		/* b. Operate on the queue */
219		*is_full = ia_css_circbuf_desc_is_full(&cb_desc);
220		return 0;
221	}
222
223	return -EINVAL;
224}
225
226int ia_css_queue_get_free_space(ia_css_queue_t *qhandle, uint32_t *size)
227{
228	int error;
229
230	if ((!qhandle) || (!size))
231		return -EINVAL;
232
233	/* 1. Load the required queue object */
234	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
235		/* Directly de-ref the object and
236		 * operate on the queue
237		 */
238		*size = ia_css_circbuf_get_free_elems(&qhandle->desc.cb_local);
239		return 0;
240	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
241		/* a. Load the queue from remote */
242		ia_css_circbuf_desc_t cb_desc;
243		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
244
245		QUEUE_CB_DESC_INIT(&cb_desc);
246		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
247		if (error != 0)
248			return error;
249
250		/* b. Operate on the queue */
251		*size = ia_css_circbuf_desc_get_free_elems(&cb_desc);
252		return 0;
253	}
254
255	return -EINVAL;
256}
257
258int ia_css_queue_get_used_space(ia_css_queue_t *qhandle, uint32_t *size)
259{
260	int error;
261
262	if ((!qhandle) || (!size))
263		return -EINVAL;
264
265	/* 1. Load the required queue object */
266	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
267		/* Directly de-ref the object and
268		 * operate on the queue
269		 */
270		*size = ia_css_circbuf_get_num_elems(&qhandle->desc.cb_local);
271		return 0;
272	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
273		/* a. Load the queue from remote */
274		ia_css_circbuf_desc_t cb_desc;
275		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
276
277		QUEUE_CB_DESC_INIT(&cb_desc);
278		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
279		if (error != 0)
280			return error;
281
282		/* b. Operate on the queue */
283		*size = ia_css_circbuf_desc_get_num_elems(&cb_desc);
284		return 0;
285	}
286
287	return -EINVAL;
288}
289
290int ia_css_queue_peek(ia_css_queue_t *qhandle, u32 offset, uint32_t *element)
291{
292	u32 num_elems;
293	int error;
294
295	if ((!qhandle) || (!element))
296		return -EINVAL;
297
298	/* 1. Load the required queue object */
299	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
300		/* Directly de-ref the object and
301		 * operate on the queue
302		 */
303		/* Check if offset is valid */
304		num_elems = ia_css_circbuf_get_num_elems(&qhandle->desc.cb_local);
305		if (offset > num_elems)
306			return -EINVAL;
307
308		*element = ia_css_circbuf_peek_from_start(&qhandle->desc.cb_local, (int)offset);
309		return 0;
310	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
311		/* a. Load the queue from remote */
312		ia_css_circbuf_desc_t cb_desc;
313		ia_css_circbuf_elem_t cb_elem;
314		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
315
316		QUEUE_CB_DESC_INIT(&cb_desc);
317
318		error =  ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
319		if (error != 0)
320			return error;
321
322		/* Check if offset is valid */
323		num_elems = ia_css_circbuf_desc_get_num_elems(&cb_desc);
324		if (offset > num_elems)
325			return -EINVAL;
326
327		offset = OP_std_modadd(cb_desc.start, offset, cb_desc.size);
328		error = ia_css_queue_item_load(qhandle, (uint8_t)offset, &cb_elem);
329		if (error != 0)
330			return error;
331
332		*element = cb_elem.val;
333		return 0;
334	}
335
336	return -EINVAL;
337}
338
339int ia_css_queue_is_empty(ia_css_queue_t *qhandle, bool *is_empty)
340{
341	int error;
342
343	if ((!qhandle) || (!is_empty))
344		return -EINVAL;
345
346	/* 1. Load the required queue object */
347	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
348		/* Directly de-ref the object and
349		 * operate on the queue
350		 */
351		*is_empty = ia_css_circbuf_is_empty(&qhandle->desc.cb_local);
352		return 0;
353	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
354		/* a. Load the queue from remote */
355		ia_css_circbuf_desc_t cb_desc;
356		u32 ignore_desc_flags = QUEUE_IGNORE_STEP_FLAG;
357
358		QUEUE_CB_DESC_INIT(&cb_desc);
359		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
360		if (error != 0)
361			return error;
362
363		/* b. Operate on the queue */
364		*is_empty = ia_css_circbuf_desc_is_empty(&cb_desc);
365		return 0;
366	}
367
368	return -EINVAL;
369}
370
371int ia_css_queue_get_size(ia_css_queue_t *qhandle, uint32_t *size)
372{
373	int error;
374
375	if ((!qhandle) || (!size))
376		return -EINVAL;
377
378	/* 1. Load the required queue object */
379	if (qhandle->type == IA_CSS_QUEUE_TYPE_LOCAL) {
380		/* Directly de-ref the object and
381		 * operate on the queue
382		 */
383		/* Return maximum usable capacity */
384		*size = ia_css_circbuf_get_size(&qhandle->desc.cb_local);
385	} else if (qhandle->type == IA_CSS_QUEUE_TYPE_REMOTE) {
386		/* a. Load the queue from remote */
387		ia_css_circbuf_desc_t cb_desc;
388		u32 ignore_desc_flags = QUEUE_IGNORE_START_END_STEP_FLAGS;
389
390		QUEUE_CB_DESC_INIT(&cb_desc);
391
392		error = ia_css_queue_load(qhandle, &cb_desc, ignore_desc_flags);
393		if (error != 0)
394			return error;
395
396		/* Return maximum usable capacity */
397		*size = cb_desc.size;
398	}
399
400	return 0;
401}
402