/*
   forward-v4l2.h - Video4Linux2 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_V4L2_H
#define FORWARD_V4L2_H

#include "forward.h"
#include "forward-v4l2-timings.h"
#include "forward-v4l2-ioctl.h"
#include "forward-irq.h"

#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>

#include <media/videobuf2-core.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
#include <media/videobuf2-v4l2.h>
#endif

#include <uapi/linux/videodev2.h>

#define FORWARD_V4L2_MIN_BUFFERS 3
#define FORWARD_V4L2_MODULE_NAME "forward-v4l2"

#define FORWARD_V4L2_ASI_DEFAULT_BUFFER_SIZE (1024 * 1024)

#define FORWARD_V4L2_EVENT_QUEUE_SIZE 8

#define FORWARD_V4L2_IRQ_PRIORITY 0

struct forward_v4l2_dev_ops;
struct forward_v4l2;
struct forward_v4l2_io;
struct forward_v4l2_timings;
struct forward_v4l2_genlock;

enum forward_v4l2_pixfmt_conv {
	FORWARD_V4L2_CONV_NONE = 0,
	FORWARD_V4L2_CONV_UYVY_YUYV,
	FORWARD_V4L2_CONV_HDMI420_YUV420,
	FORWARD_V4L2_CONV_10BIT_V210,
	FORWARD_V4L2_CONV_10BIT_16BIT
};

struct forward_v4l2_analog_in {
	struct forward_v4l2 *v4l2;
	struct forward_v4l2_timings timings;
	bool no_signal;
	bool timings_changed;
	enum v4l2_forward_analog_rx_mode mode, new_mode;
	bool non_master;
};

struct forward_v4l2 {
	struct forward_dev *dev;

	struct v4l2_device v4l2_dev;

	struct forward_v4l2_io *io;
	struct forward_v4l2_dev_ops *ops;

	u64 clk_deviation;

	struct delayed_work work;
	struct forward_v4l2_analog_in *analog;
};

struct forward_v4l2_buffer {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
	struct vb2_v4l2_buffer vb;
#else
	struct vb2_buffer vb;
#endif
	int video_size[2];
	int vbi_size[2];
	struct list_head list;
};

struct forward_v4l2_dev_ops {
	//! Board-specific io init
	int (*init_io)(struct forward_v4l2_io *io);
	//! Board-specific io deinit
	void (*fini_io)(struct forward_v4l2_io *io);

	//! IRQ Mask for io
	forward_irq_t (*irq_mask)(const struct forward_v4l2_io *io);

	//! Extract info from irq flags
	bool (*irq_info)(struct forward_v4l2_io *io, const forward_irq_t *irq, int *cur_buf,
			 int *prev_buf, int *next_buf, int *vbi_size, int *video_size);

	//! Get VDMA region info
	void (*region_info)(const struct forward_v4l2_io *io, u32 *address, size_t *size);

	//! Get buffer VDMA addresses
	void (*buffer_info)(const struct forward_v4l2_io *io, int buffer, u32 *address,
			    size_t *size);

	//! Timing can be used by input
	bool (*timings_valid)(const struct forward_v4l2_io *io,
			      const struct forward_v4l2_timings *t);

	//! Query current timing on input
	int (*timings_query)(const struct forward_v4l2_io *io, struct forward_v4l2_timings *t);

	int (*timings_set)(const struct forward_v4l2_io *io, const struct forward_v4l2_timings *t);

	//! Check that timing changed on input
	bool (*timings_changed)(const struct forward_v4l2_io *io,
				const struct forward_v4l2_timings *old,
				struct forward_v4l2_timings *new);

	//! Correct v4l2_pix_format according to input
	void (*check_fmt)(const struct forward_v4l2_io *io, struct v4l2_pix_format *f);
	//! Enum all supported v4l2_pix_format for input
	int (*enum_fmt)(const struct forward_v4l2_io *io, int index, u32 *pix_fmt, char *desc,
			size_t desc_len);
	//! Set v4l2_pix_format
	void (*set_fmt)(struct forward_v4l2_io *io, struct v4l2_pix_format *f);

	//! Update frame meta information (VPID, etc.)
	int (*update_meta)(struct forward_v4l2_io *io);
	//! Update next frame timecode
	void (*update_timecode)(struct forward_v4l2_io *io, u64 timecode, u16 ddb);

	//! Prepare streaming
	void (*prepare)(struct forward_v4l2_io *io);

	//! Start streaming
	void (*start)(struct forward_v4l2_io *io);

	//! Stop streaming
	void (*stop)(struct forward_v4l2_io *io);

	//! Check signal presented on input
	bool (*signal_present)(const struct forward_v4l2_io *io);

	u32 (*io_timestamp)(const struct forward_v4l2_io *io);
	u32 (*hw_timestamp)(struct forward_v4l2 *dev);
	s64 (*clock_deviation)(struct forward_v4l2 *dev);
	s64 (*set_clock_deviation)(struct forward_v4l2 *dev, s64 ppt);

	//! Configure global genlock mode
	int (*genlock_set_global_mode)(const struct forward_v4l2 *dev, bool master,
				       struct forward_v4l2_timings *t);
	//! Configure genlock mode
	int (*genlock_switch)(const struct forward_v4l2_genlock *gl,
			      enum v4l2_forward_genlock_source src);
	//! Move sync generator forward (positive) / backward (negative)
	void (*genlock_sync_offset)(const struct forward_v4l2_genlock *gl, s32 offset,
				    bool comp_delay);

	//! Toggle output clone
	int (*toggle_clone)(const struct forward_v4l2_io *io, bool clone);
	//! Toggle in->out bypass
	int (*toggle_bypass)(const struct forward_v4l2_io *io, bool enable, int timeout_ms);
	//! Toggle output mute
	int (*toggle_mute)(const struct forward_v4l2_io *io, bool mute);
	//! Toggle output watchdog
	int (*toggle_watchdog)(const struct forward_v4l2_io *io, bool enable);
	//! Output watchdog keep-alive
	void (*watchdog_keepalive)(const struct forward_v4l2_io *io, int timeout_ms);

	//! Setup analog input mode
	int (*analog_rx_set_mode)(struct forward_v4l2 *dev, enum v4l2_forward_analog_rx_mode mode);
	//! Get current analog input mode
	enum v4l2_forward_analog_rx_mode (*analog_rx_get_mode)(struct forward_v4l2 *dev);
	//! Get data from analog input
	int (*analog_rx_timestamp)(struct forward_v4l2 *dev, bool *valid, u32 *timestamp,
				   u32 *cur_time, u64 *sys_time);
	//! Get timecode from analog input
	int (*analog_rx_timecode)(struct forward_v4l2 *dev, bool *valid, struct v4l2_timecode *tc);
	//! Get analog input video timings
	int (*analog_rx_video_query)(struct forward_v4l2 *dev, struct forward_v4l2_timings *t);

	//! Number of buffers
	int hw_buffers;
	bool vbi_support;
};

#endif
