// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Intel Camera Imaging ISP subsystem.
 * Copyright (c) 2010 - 2015, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 */

#include "hmm.h"

#include "type_support.h"
#include "queue_access.h"
#include "ia_css_circbuf.h"
#include "sp.h"
#include "assert_support.h"

int ia_css_queue_load(
    struct ia_css_queue *rdesc,
    ia_css_circbuf_desc_t *cb_desc,
    uint32_t ignore_desc_flags)
{
	if (!rdesc || !cb_desc)
		return -EINVAL;

	if (rdesc->location == IA_CSS_QUEUE_LOC_SP) {
		assert(ignore_desc_flags <= QUEUE_IGNORE_DESC_FLAGS_MAX);

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_SIZE_FLAG)) {
			cb_desc->size = sp_dmem_load_uint8(rdesc->proc_id,
							   rdesc->desc.remote.cb_desc_addr
							   + offsetof(ia_css_circbuf_desc_t, size));

			if (cb_desc->size == 0) {
				/* Adding back the workaround which was removed
				   while refactoring queues. When reading size
				   through sp_dmem_load_*, sometimes we get back
				   the value as zero. This causes division by 0
				   exception as the size is used in a modular
				   division operation. */
				return -EDOM;
			}
		}

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_START_FLAG))
			cb_desc->start = sp_dmem_load_uint8(rdesc->proc_id,
							    rdesc->desc.remote.cb_desc_addr
							    + offsetof(ia_css_circbuf_desc_t, start));

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_END_FLAG))
			cb_desc->end = sp_dmem_load_uint8(rdesc->proc_id,
							  rdesc->desc.remote.cb_desc_addr
							  + offsetof(ia_css_circbuf_desc_t, end));

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_STEP_FLAG))
			cb_desc->step = sp_dmem_load_uint8(rdesc->proc_id,
							   rdesc->desc.remote.cb_desc_addr
							   + offsetof(ia_css_circbuf_desc_t, step));

	} else if (rdesc->location == IA_CSS_QUEUE_LOC_HOST) {
		/* doing DMA transfer of entire structure */
		hmm_load(rdesc->desc.remote.cb_desc_addr,
			  (void *)cb_desc,
			  sizeof(ia_css_circbuf_desc_t));
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_ISP) {
		/* Not supported yet */
		return -ENOTSUPP;
	}

	return 0;
}

int ia_css_queue_store(
    struct ia_css_queue *rdesc,
    ia_css_circbuf_desc_t *cb_desc,
    uint32_t ignore_desc_flags)
{
	if (!rdesc || !cb_desc)
		return -EINVAL;

	if (rdesc->location == IA_CSS_QUEUE_LOC_SP) {
		assert(ignore_desc_flags <= QUEUE_IGNORE_DESC_FLAGS_MAX);

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_SIZE_FLAG))
			sp_dmem_store_uint8(rdesc->proc_id,
					    rdesc->desc.remote.cb_desc_addr
					    + offsetof(ia_css_circbuf_desc_t, size),
					    cb_desc->size);

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_START_FLAG))
			sp_dmem_store_uint8(rdesc->proc_id,
					    rdesc->desc.remote.cb_desc_addr
					    + offsetof(ia_css_circbuf_desc_t, start),
					    cb_desc->start);

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_END_FLAG))
			sp_dmem_store_uint8(rdesc->proc_id,
					    rdesc->desc.remote.cb_desc_addr
					    + offsetof(ia_css_circbuf_desc_t, end),
					    cb_desc->end);

		if (0 == (ignore_desc_flags & QUEUE_IGNORE_STEP_FLAG))
			sp_dmem_store_uint8(rdesc->proc_id,
					    rdesc->desc.remote.cb_desc_addr
					    + offsetof(ia_css_circbuf_desc_t, step),
					    cb_desc->step);
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_HOST) {
		/* doing DMA transfer of entire structure */
		hmm_store(rdesc->desc.remote.cb_desc_addr,
			   (void *)cb_desc,
			   sizeof(ia_css_circbuf_desc_t));
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_ISP) {
		/* Not supported yet */
		return -ENOTSUPP;
	}

	return 0;
}

int ia_css_queue_item_load(
    struct ia_css_queue *rdesc,
    u8 position,
    ia_css_circbuf_elem_t *item)
{
	if (!rdesc || !item)
		return -EINVAL;

	if (rdesc->location == IA_CSS_QUEUE_LOC_SP) {
		sp_dmem_load(rdesc->proc_id,
			     rdesc->desc.remote.cb_elems_addr
			     + position * sizeof(ia_css_circbuf_elem_t),
			     item,
			     sizeof(ia_css_circbuf_elem_t));
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_HOST) {
		hmm_load(rdesc->desc.remote.cb_elems_addr
			  + position * sizeof(ia_css_circbuf_elem_t),
			  (void *)item,
			  sizeof(ia_css_circbuf_elem_t));
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_ISP) {
		/* Not supported yet */
		return -ENOTSUPP;
	}

	return 0;
}

int ia_css_queue_item_store(
    struct ia_css_queue *rdesc,
    u8 position,
    ia_css_circbuf_elem_t *item)
{
	if (!rdesc || !item)
		return -EINVAL;

	if (rdesc->location == IA_CSS_QUEUE_LOC_SP) {
		sp_dmem_store(rdesc->proc_id,
			      rdesc->desc.remote.cb_elems_addr
			      + position * sizeof(ia_css_circbuf_elem_t),
			      item,
			      sizeof(ia_css_circbuf_elem_t));
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_HOST) {
		hmm_store(rdesc->desc.remote.cb_elems_addr
			   + position * sizeof(ia_css_circbuf_elem_t),
			   (void *)item,
			   sizeof(ia_css_circbuf_elem_t));
	} else if (rdesc->location == IA_CSS_QUEUE_LOC_ISP) {
		/* Not supported yet */
		return -ENOTSUPP;
	}

	return 0;
}