/*
   forward-v4l2-timings.h - v4l2 timings 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_V4L2_TIMINGS_H
#define FORWARD_V4L2_TIMINGS_H

#include <uapi/linux/videodev2.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
#include <media/v4l2-dv-timings.h>
#else
#define V4L2_DV_BT_BLANKING_WIDTH(bt) ((bt)->hfrontporch + (bt)->hsync + (bt)->hbackporch)
#define V4L2_DV_BT_FRAME_WIDTH(bt) ((bt)->width + V4L2_DV_BT_BLANKING_WIDTH(bt))
#define V4L2_DV_BT_BLANKING_HEIGHT(bt)                                               \
	((bt)->vfrontporch + (bt)->vsync + (bt)->vbackporch + (bt)->il_vfrontporch + \
	 (bt)->il_vsync + (bt)->il_vbackporch)
#define V4L2_DV_BT_FRAME_HEIGHT(bt) ((bt)->height + V4L2_DV_BT_BLANKING_HEIGHT(bt))
bool v4l2_match_dv_timings(const struct v4l2_dv_timings *t1, const struct v4l2_dv_timings *t2,
			   unsigned pclock_delta);
typedef bool v4l2_check_dv_timings_fnc(const struct v4l2_dv_timings *t, void *handle);
int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, const struct v4l2_dv_timings_cap *cap,
			     v4l2_check_dv_timings_fnc fnc, void *fnc_handle);
extern const struct v4l2_dv_timings v4l2_dv_timings_presets[];
#endif

#define FORWARD_SDI_TIMINGS_F_ASI (1 << 0)

enum forward_timing_type { FORWARD_TIMING_INVALID = 0, FORWARD_TIMING_SDI, FORWARD_TIMING_HDMI };

enum forward_sdi_mode {
	FORWARD_SDI_MODE_INVALID = 0,
	FORWARD_SDI_MODE_SD_SDI,
	FORWARD_SDI_MODE_HD_SDI,
	FORWARD_SDI_MODE_3G_SDI,
	FORWARD_SDI_MODE_6G_SDI,
	FORWARD_SDI_MODE_12G_SDI,
	FORWARD_SDI_MODE_DUAL_3G_SDI,
	FORWARD_SDI_MODE_DUAL_6G_SDI,
	FORWARD_SDI_MODE_QUAD_3G_SDI
};

struct forward_sdi_timings {
	enum forward_sdi_mode mode;
	bool m;
	bool interlaced;
	int width;
	int height;
	int active_width;
	int active_height[2];
	int active_starts[2];
	int active_stops[2];
	int field_switch[2];
	int topVBIs[2];
	int bottomVBIs[2];
	u32 flags;
};

struct forward_v4l2_timings {
	enum forward_timing_type type;
	union {
		struct v4l2_dv_timings hdmi;
		struct forward_sdi_timings sdi;
	};
};

extern const struct forward_v4l2_timings forward_v4l2_timings[];
extern const int forward_v4l2_timings_count;

unsigned int forward_v4l2_total_pixels(const struct forward_v4l2_timings *t);
unsigned int forward_v4l2_total_height(const struct forward_v4l2_timings *t);
unsigned int forward_v4l2_total_width(const struct forward_v4l2_timings *t);
unsigned int forward_v4l2_pixel_clock(const struct forward_v4l2_timings *t);
bool forward_v4l2_interlaced(const struct forward_v4l2_timings *t);
unsigned int forward_v4l2_visible_height(const struct forward_v4l2_timings *t);
unsigned int forward_v4l2_visible_width(const struct forward_v4l2_timings *t);
struct v4l2_fract forward_v4l2_frameinterval(const struct forward_v4l2_timings *t);
struct v4l2_fract forward_v4l2_fps(const struct forward_v4l2_timings *t);

void forward_v4l2_fill_pix_fmt(const struct forward_v4l2_timings *t, struct v4l2_pix_format *f);
void forward_v4l2_fill_vbi_fmt(const struct forward_v4l2_timings *t, struct v4l2_vbi_format *fmt);
void forward_v4l2_fill_dv_timings(const struct forward_v4l2_timings *t, struct v4l2_dv_timings *dv);
bool forward_v4l2_dv_match(const struct forward_v4l2_timings *t, const struct v4l2_dv_timings *dv);
bool forward_v4l2_timings_fps_divisible(const struct forward_v4l2_timings *t1,
					const struct forward_v4l2_timings *t2);

bool forward_v4l2_timing_guess_sdi(enum forward_sdi_mode mode, bool interlaced, int width,
				   int height, int active_width, int active_height, u32 vpid,
				   u64 pixel_clk, struct forward_v4l2_timings *t);

bool forward_v4l2_timing_guess_hdmi(bool interlaced, int width, int height, int active_width,
				    int active_height, u8 vic, u64 pixel_clk,
				    struct forward_v4l2_timings *t);

bool forward_v4l2_timing_guess_analog(int hsync_freq, int vsync_freq1000,
				      struct forward_v4l2_timings *t);

bool forward_v4l2_timings_equal(const struct forward_v4l2_timings *x,
				const struct forward_v4l2_timings *y, bool m_match);

u32 forward_v4l2_timings_vpid(const struct forward_v4l2_timings *t, bool level_b, bool depth10,
			      bool widescreen);

void forward_v4l2_timings_print(const struct forward_v4l2_timings *t);

#endif // FORWARD_V4L2_TIMINGS_H
