/*
   forward.h - driver for SoftLab-NSK Forward 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_H
#define FORWARD_H

#include <linux/kernel.h>
#include <linux/bitmap.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/irq.h>

#include "forward-version.h"

#define FORWARD_CLASS_NAME "forward"

#define FORWARD_MAX_DEVICES 32
#define FORWARD_MAX_SUBDEVICES 16
#define FORWARD_NAME_LENGTH 64
#define FORWARD_MAX_MODE 8
#define FORWARD_MAX_MODE_STRING (FORWARD_MAX_MODE * 16)
#define FORWARD_MAX_IO 16
#define FORWARD_DMA_LATENCY_US 100
#define FORWARD_IRQ_FLAG_BITS 128
#define FORWARD_IRQ_MAX_WQ 64
#define FORWARD_IRQ_IO_PER_WQ 1

#define FORWARD_MAX_EXTENSION_NAME 63

struct forward_vdma;
struct forward_sdma;
struct forward_dev;

enum forward_dev_type {
	FORWARD_FD722 = 0,
	FORWARD_FD788 = 1,
	FORWARD_FD720 = 2,
	FORWARD_FD922 = 3,
	FORWARD_FDIO = 4,
	FORWARD_FD940 = 5,
	FORWARD_FD2110 = 6,
	FORWARD_FD722M2 = 7,
	FORWARD_TYPE_COUNT = 8
};

enum forward_io_type { FORWARD_IO_INPUT, FORWARD_IO_OUTPUT, FORWARD_IO_BIDIR };

enum forward_io_state { FORWARD_IO_DISABLED, FORWARD_IO_TX, FORWARD_IO_RX, FORWARD_IO_TXRX };

typedef struct forward_irq_t {
	unsigned long irq[BITS_TO_LONGS(FORWARD_IRQ_FLAG_BITS)];
} forward_irq_t;

struct forward_dev_config {
	int vdma_size_mb;
	int sdma_size;
	int sdma_rings;
	int io_count;
	const struct attribute_group *attributes;
	void *private;

	int (*init)(struct forward_dev *dev);
	void (*fini)(struct forward_dev *dev);
	void (*reboot)(struct forward_dev *dev);

	void (*read_id)(struct forward_dev *dev);

	void (*enable_interrupts)(struct forward_dev *dev, const forward_irq_t *interrupts);
	bool (*read_interrupts)(struct forward_dev *dev, forward_irq_t *interrupts,
				forward_irq_t *data, forward_irq_t *data_mask);
	void (*disable_interrupts)(struct forward_dev *dev, const forward_irq_t *interrupts);

	void (*enable_vdma)(struct forward_dev *dev, u32 map_addr);
	void (*disable_vdma)(struct forward_dev *dev);

	enum forward_io_type (*io_type)(struct forward_dev *dev, int io);
	int (*io_number)(struct forward_dev *dev, int io);
	enum forward_io_state (*io_state)(struct forward_dev *dev, int io);
	int (*set_io_state)(struct forward_dev *dev, int io, enum forward_io_state state);

	void (*toggle_streaming)(struct forward_dev *dev, int io, bool enabled);

	void (*enable_sdma)(struct forward_dev *dev, u32 map_addr);
	void (*disable_sdma)(struct forward_dev *dev);
	void (*sdma_enable_ring)(struct forward_dev *dev, int ring);
	void (*sdma_disable_ring)(struct forward_dev *dev, int ring);
	void (*sdma_get_hw_pointer)(struct forward_dev *dev, int ring, u16 *hw);
	void (*sdma_set_pointers)(struct forward_dev *dev, int ring, u16 hw, bool update_hw, u16 sw,
				  bool update_sw);
};

struct forward_io {
	struct forward_dev *dev;
	struct kref ref;
	struct mutex lock;
	struct kref streaming_ref;
	int index;
	int number;
	enum forward_io_state state;
	void *v4l2;
	void *alsa;
};

struct forward_dev {
	struct pci_dev *pci_dev;
	struct platform_device *plat_dev;
	struct device *dev;
	struct device *parent_dev;
	struct cdev ctl_dev;
	int irq;

	struct list_head list;
	int number;

	struct mutex lock;
	struct kref ref;
	spinlock_t ref_lock;

	enum forward_dev_type type;
	char name[FORWARD_NAME_LENGTH];
	char dev_name[FORWARD_NAME_LENGTH];
	char *mode[FORWARD_MAX_MODE];
	char mode_string[FORWARD_MAX_MODE_STRING];

	bool (*i2c_xfer)(struct forward_dev *dev, u8 bus, bool read, u8 addr, u8 subaddr, u8 *data);
	bool (*spi_xfer)(struct forward_dev *dev, u8 bus, u8 *tx, int tx_bits, u8 *rx, int rx_bits);

	s64 soft_id;
	u64 hard_id;
	u32 fw_version;

	u32 *csr;
	size_t csr_size;
	struct forward_dev_config cfg;

	u16 mcap_addr;
	u32 mcap_fw_version;

	struct list_head listeners;
	spinlock_t listeners_lock;
	struct workqueue_struct *irq_wqs[FORWARD_IRQ_MAX_WQ];
	int irq_wq_count;

	struct list_head extensions;
	spinlock_t extensions_lock;

	struct forward_vdma *vdma;
	struct forward_sdma *sdma;
	struct forward_pll *pll;
	struct forward_io *io;

	const struct attribute_group *attr_groups[3];
};

struct forward_extension {
	const char *name;
	const u32 version;
	bool (*available)(struct forward_dev *dev);
	int (*probe)(struct forward_dev *dev, void **private);
	int (*reprobe)(struct forward_dev *dev, void **private);
	void (*remove)(struct forward_dev *dev, void **private);
	void (*io_changed)(struct forward_dev *dev, struct forward_io *io,
			   enum forward_io_state state, void **private);
	struct list_head node;
};

#define forward_err(fdev, fmt, ...) dev_err((fdev)->dev, fmt, ##__VA_ARGS__)
#define forward_warn(fdev, fmt, ...) dev_warn((fdev)->dev, fmt, ##__VA_ARGS__)
#define forward_notice(fdev, fmt, ...) dev_notice((fdev)->dev, fmt, ##__VA_ARGS__)
#define forward_info(fdev, fmt, ...) dev_info((fdev)->dev, fmt, ##__VA_ARGS__)
#define forward_dbg(fdev, fmt, ...) dev_dbg((fdev)->dev, fmt, ##__VA_ARGS__)

struct class *forward_class(void);
dev_t forward_dev_number(struct forward_dev *dev, int subdev);

int forward_ref_get(struct forward_dev *dev);
void forward_ref_put(struct forward_dev *dev);
int forward_ref_read(struct forward_dev *dev);

int forward_io_ref_get(struct forward_io *io);
void forward_io_ref_put(struct forward_io *io);
int forward_io_ref_read(struct forward_io *io);

int forward_switch_mode(struct forward_dev *dev, const char *mode_string);
bool forward_has_mode(struct forward_dev *dev, const char *mode);

int forward_reboot_device(struct forward_dev *dev);

int forward_switch_io(struct forward_io *io, enum forward_io_state state);

int forward_toggle_streaming(struct forward_io *io, bool enabled);

void *forward_extension_private(struct forward_dev *dev, const struct forward_extension *ext);

int forward_register_extension(struct forward_extension *ext);
void forward_unregister_extension(struct forward_extension *ext);

const char *forward_dev_model_name(struct forward_dev *dev);

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))
#endif

#endif
