/*
   forward-vdma.h - StreamDMA driver for SoftLab-NSK Forward video boards

   Copyright (C) 2017 - 2024 SoftLab-NSK <forward@softlab.tv>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef FORWARD_SDMA_H
#define FORWARD_SDMA_H

#include "forward.h"

struct forward_sdma;

typedef struct _sdma_desc_t {
	u64 address : 44;
	u16 _unused4548 : 4;
	u16 length : 16;
	u64 timestamp : 48;
	u8 cmd : 8;
	u8 status : 8;
} __attribute__((packed)) sdma_desc_t;

struct forward_sdma_io_ring {
	struct forward_sdma *sdma;
	uint8_t id;
	uint16_t size;
	uint16_t sw, hw;
	sdma_desc_t *desc;
	void **cookies;
};

struct forward_sdma {
	struct forward_dev *dev;
	bool dma44bit;

	unsigned int map_size;
	u32 *map;
	dma_addr_t map_addr;

	sdma_desc_t *descriptors;
	void **cookies;

	sdma_desc_t dummy;
	u32 *dummy_data;
	dma_addr_t dummy_addr;

	unsigned int num_rings;
	struct forward_sdma_io_ring *rings;

	spinlock_t lock;
};

int forward_sdma_probe(struct forward_dev *dev);
void forward_sdma_remove(struct forward_dev *dev);

void forward_sdma_enable(struct forward_dev *dev);
void forward_sdma_disable(struct forward_dev *dev);

void forward_sdma_enable_ring(struct forward_sdma_io_ring *ring);
void forward_sdma_disable_ring(struct forward_sdma_io_ring *ring);

void forward_sdma_update(struct forward_sdma_io_ring *ring);
void forward_sdma_reset(struct forward_sdma_io_ring *ring);
void forward_sdma_advance(struct forward_sdma_io_ring *ring, u16 amount);

static inline sdma_desc_t forward_sdma_set_descriptor(struct forward_sdma_io_ring *ring, u16 idx,
						      dma_addr_t addr, u16 len, u8 cmd,
						      void *cookie)
{
	sdma_desc_t desc = { 0 };
	desc.address = addr;
	desc.length = len;
	desc.cmd = cmd;

	ring->cookies[idx] = cookie;
	ring->desc[idx] = desc;

	return desc;
}

static inline sdma_desc_t forward_sdma_get_descriptor(struct forward_sdma_io_ring *ring, u16 idx,
						      dma_addr_t *addr, u16 *len, u8 *status,
						      u64 *timestamp, void **cookie)
{
	sdma_desc_t desc;

	desc = ring->desc[idx];
	*cookie = ring->cookies[idx];

	*addr = desc.address;
	*len = desc.length;
	*status = desc.status;
	*timestamp = desc.timestamp;

	return desc;
}

void forward_sdma_debug_show(struct forward_sdma *sdma);

#endif
