/*
   forward-v4l2.h - Video4Linux2 driver for SoftLab-NSK Forward boards

   Copyright (C) 2017 - 2023 Konstantin Oblaukhov <oblaukhov@sl.iae.nsk.su>

   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
};

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_genlock *genlock;
};

struct forward_v4l2_buffer {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
	struct vb2_v4l2_buffer vb;
#else
	struct vb2_buffer vb;
#endif
	struct list_head list;
};

struct forward_v4l2_dev_ops {
	//! IRQ Mask for io
	u32 (*irq_mask)(const struct forward_v4l2_io *io);

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

	//! 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);

	//! 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);

	//! 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);

	//! Query current timing on genlock input
	int (*genlock_query)(const struct forward_v4l2_genlock *gl,
			     enum v4l2_forward_genlock_source src, struct forward_v4l2_timings *t);
	//! Configure genlock mode
	int (*genlock_switch)(const struct forward_v4l2_genlock *gl,
			      enum v4l2_forward_genlock_source src, struct forward_v4l2_timings *t);
	//! Sync generator irq
	u32 (*genlock_sync_irq_mask)(const struct forward_v4l2_genlock *gl, int num);
	//! Extract sync generator irq data
	bool (*genlock_sync_irq_info)(const struct forward_v4l2_genlock *gl, int num, u32 irq,
				      bool *field, s32 *gl_offset);
	//! Move sync generator forward (positive) / backward (negative)
	void (*genlock_sync_offset)(const struct forward_v4l2_genlock *gl, int num, s32 offset,
				    bool comp_delay);
	//! Enable/disable output syncing
	void (*genlock_sync_output)(const struct forward_v4l2_genlock *gl,
				    struct forward_v4l2_io *io);

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

#endif
