/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
 *
 * Copyright (c) 2023 MediaTek Inc. All rights reserved.
 */

#ifndef MTK_SRCCAP_DPRX_H
#define MTK_SRCCAP_DPRX_H

#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/mtk-v4l2-srccap.h>

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

#define DPRX_SUBDEV_NAME			"mtk_dprx"
#define DPRX_TASK_SLEEP_TIME			500
#define DPRX_NOT_READY_TASK_SLEEP		1000
#define DPRX_NOT_READY_TASK_CNT			20
#define DPRX_TIMER_MS				8 // actually 12 ms when HZ = 250

#define EDID_PER_BLOCK_SIZE			128
#define DPRX_EDID_SIZE_MAX			512

#define DPRX_OUTPUT_CRC_INDEX			0x6 // u8 * 6
#define DPRX_INPUT_DSC_CRC_INDEX		0x3 // u16 * 3

// FIXME:
#define DPRX_NOT_READY_FOR_IRQ_AND_CLK

#define DPRX_TIMING_DATA_HTT_INDEX		0
#define DPRX_TIMING_DATA_VTT_INDEX		1
#define DPRX_TIMING_DATA_H_WIDTH_INDEX		2
#define DPRX_TIMING_DATA_V_WIDTH_INDEX		3
#define DPRX_TIMING_DATA_H_START_INDEX		4
#define DPRX_TIMING_DATA_V_START_INDEX		5
#define DPRX_TIMING_DATA_HSYNC_WIDTH_INDEX	6
#define DPRX_TIMING_DATA_VSYNC_WIDTH_INDEX	7
#define DPRX_TIMING_DATA_H_BACK_PORCH_INDEX	8
#define DPRX_TIMING_DATA_V_BACK_PORCH_INDEX	9
#define DPRX_TIMING_DATA_INTERLACE_INDEX	10
#define DPRX_TIMING_DATA_H_POLARITY_INDEX	11
#define DPRX_TIMING_DATA_V_POLARITY_INDEX	12
#define DPRX_TIMING_DATA_LENGTH			13 // magic number in hwreg

#define DPRX_HW_DETECT_TIMING_HTT_INDEX		0
#define DPRX_HW_DETECT_TIMING_VTT_INDEX		1
#define DPRX_HW_DETECT_TIMING_HWIDTH_INDEX	2
#define DPRX_HW_DETECT_TIMING_VHEIGHT_INDEX	3
#define DPRX_HW_DETECT_TIMING_LENGTH		4 // magic number in hwreg

#define DPRX_HDCP1X_AKSV_SIZE			5
#define DPRX_HDCP1X_RO_SIZE			2
#define DPRX_HDCP1X_AN_SIZE			8
#define DPRX_HDCP1X_BCAPS_SIZE			1
#define DPRX_HDCP1X_BINFO_SIZE			2
#define DPRX_HDCP1X_BKSV_SIZE			5
#define DPRX_HDCP1X_BSTATUS_SIZE		1
#define DPRX_HDCP1X_M0_SIZE			8
#define DPRX_HDCP2X_RX_INFO_SIZE		2
#define DPRX_HDCP2X_AKE_RTX_SIZE		8
#define DPRX_HDCP2X_AKE_TXCAPS_SIZE		3

// VBID bit
#define DPRX_VBID_VERTICAL_BLANKING_FLAG	0
#define DPRX_VBID_FIELD_ID_FLAG			1
#define DPRX_VBID_INTERLACE_FLAG		2
#define DPRX_VBID_NO_VIDEO_STREAM_FLAG		3
#define DPRX_VBID_AUDIO_MUTE_FLAG		4
#define DPRX_VBID_HDCP_SYNC_DETECT		5
#define DPRX_VBID_COMPRESSED_STREAM_FLAG	6
#define DPRX_VBID_RESERVED			7

#define DPRX_LOG_EMERG				(1 << 0)
#define DPRX_LOG_ERR				(1 << 1)
#define DPRX_LOG_WARN				(1 << 2)
#define DPRX_LOG_INFO				(1 << 3)
#define DPRX_LOG_DEBUG				(1 << 4)

#define dprx_emerg(fmt, args...)	DPRX_LOG(DPRX_LOG_EMERG, fmt, ##args)
#define dprx_err(fmt, args...)		DPRX_LOG(DPRX_LOG_ERR, fmt, ##args)
#define dprx_warn(fmt, args...)		DPRX_LOG(DPRX_LOG_WARN, fmt, ##args)
#define dprx_info(fmt, args...)		DPRX_LOG(DPRX_LOG_INFO, fmt, ##args)
#define dprx_debug(fmt, args...)	DPRX_LOG(DPRX_LOG_DEBUG, fmt, ##args)

extern int dprx_log_level;
#define DPRX_LOG(__level__, fmt, args...)	\
	do {	\
		if (__level__ <= dprx_log_level) {	\
			switch (__level__) {	\
			case DPRX_LOG_EMERG:	\
				pr_emerg("[*] " fmt, ##args); break;	\
			case DPRX_LOG_ERR:	\
				pr_err("[error] " fmt, ##args);	break;	\
			case DPRX_LOG_WARN:	\
				pr_warn("[warn] " fmt, ##args); break;	\
			case DPRX_LOG_INFO:	\
				pr_info("[info] " fmt, ##args); break;	\
			case DPRX_LOG_DEBUG:	\
				pr_info("[debug] " fmt, ##args); break;	\
			default:	\
				break;	\
			}	\
		}	\
	} while (false)

enum {
	DPRX_IRQ_HPD_CHG,
	DPRX_IRQ_MAX
};

/* this enum type should mapping the dts interrupt layout !!! */
enum dprx_irq_type {
	DPRX_IRQ_TYPE_PHY,
	DPRX_IRQ_TYPE_MAC,
	DPRX_IRQ_TYPE_AUX,
	DPRX_IRQ_TYPE_END,
};

#define DPRX_IRQS_NUM_MAX	DPRX_IRQ_TYPE_END

struct dprx_info {
	enum dprx_version ver;

	bool hpd;
	bool cable_detect;
	enum dprx_signal_status signal_status;
	enum dprx_hdcp_type hdcp_type;

	u8 edid[512]; // FIXME: to test ltp

	int drv_port; // not used
	int ui_port; // not used
	struct v4l2_dprx_vid vid;

	struct v4l2_dprx_hwcaps hwcaps;
};

struct dprx_thread {
	struct list_head node;
	struct task_struct *task;
	struct mutex mutex;
	enum dprx_event_id type;
	u32 timers;
	void *priv;
	void (*func)(void *ptr);
};

struct dprx_device {
	struct device *dev; // main dev
	struct v4l2_subdev sd;
	struct v4l2_ctrl_handler ctrls;
	struct media_pad *pads;
	u32 nr_of_ctrls;
	u32 nr_of_pads;

	struct kobject kobj;

	/* ktest_mode means the dprx subdev bind to the *ktf (KTF),
	 * and the hwreg also be test mode.
	 */
	bool ktest_mode;

	/* test_mode means the dprx subdev bind to the *srccap (LTP),
	 * and the hwreg also be test mode.
	 */
	bool test_mode;

	u32 irq_num;
	u32 irqs[DPRX_IRQS_NUM_MAX];
	unsigned long irq_flag;

	struct clk_onecell_data clk_data;

	u32 hw_version;
	u32 input_count;

	u32 cpu_emi0_base;
	u32 riu_phy_addr;
	u64 start_bus_address;
	u64 buffer_size;
	u32 bank_dprx_phy_pm_start;
	u32 bank_dprx_phy_pm_end;
	u32 bank_dprx_aux_pm_start;
	u32 bank_dprx_aux_pm_end;
	u32 bank_dprx_phy_top_start;
	u32 bank_dprx_phy_top_end;
	u32 bank_dprx_mac_top_start; // including the dp_pll
	u32 bank_dprx_mac_top_end;
	u32 bank_dprx_dsc_top_start;
	u32 bank_dprx_dsc_top_end;
	u32 bank_dprx_ckgen00_pm_start; //for DP51
	u32 bank_dprx_ckgen00_pm_end;
	u32 bank_dprx_pm_sleep_start;
	u32 bank_dprx_pm_sleep_end;
	u32 bank_dprx_dp51_start;
	u32 bank_dprx_dp51_end;
	u32 bank_dprx_piu_misc_start;
	u32 bank_dprx_piu_misc_end;
	u32 bank_dprx_bdma_start;
	u32 bank_dprx_bdma_end; //for DP51

	struct mutex thread_lock;
	struct list_head thread_head;

	struct timer_list timer;

	//DP51
	u64 pExchangeDataPA_51;
	u32 u32ExchangeDataSize_51;

	struct dprx_info *info; // for multi-port
};

// for ktf test only
struct ktf_dprx_dev {
	struct device *dev;
	struct v4l2_device v4l2_dev;

	struct dprx_device dprx_dev; // dprx priv device

	struct kobject *kobj;

	struct mutex mutex;
};

enum dprx_hdcp1x_status {
	DPRX_HDCP1X_AKSV_RECEIVED		= _BIT(0),
	DPRX_HDCP1X_KSV_LIST_READY		= _BIT(1),
	DPRX_HDCP1X_HW_BKSV_READ_DONE		= _BIT(2),
	DPRX_HDCP1X_HW_R0_PRIME_READ_DONE	= _BIT(3),
};

enum dprx_hdcp2x_status {
	DPRX_HDCP2X_STATUS_NONE,
	DPRX_HDCP2X_INIT,
	DPRX_HDCP2X_NO_STORED_KM,
	DPRX_HDCP2X_STORED_KM,
	DPRX_HDCP2X_LC_INIT,
	DPRX_HDCP2X_SKE_SEND_EKS,
	DPRX_HDCP2X_H_PRIME_READ_DONE,
	DPRX_HDCP2X_REAUTH_SEND_ACK,
	DPRX_HDCP2X_REAUTH_STREAM_FLAG,
	DPRX_HDCP2X_READY,
	DPRX_HDCP2X_PAIRING_AVAILABLE,
	DPRX_HDCP2X_LINK_INTEGRITY_FAILURE,
	DPRX_HDCP2X_HW_WRITE_DONE,
	DPRX_HDCP2X_HW_READ_DONE,
};

struct dprx_hdcp1x_info {
	enum v4l2_srccap_input_source source;
	enum dprx_hdcp1x_status status;

	__u8 aksv[DPRX_HDCP1X_AKSV_SIZE];
	__u8 r0[DPRX_HDCP1X_RO_SIZE];
	__u8 an[DPRX_HDCP1X_AN_SIZE];
	__u8 bcaps[DPRX_HDCP1X_BCAPS_SIZE];
	__u8 binfo[DPRX_HDCP1X_BINFO_SIZE];
	__u8 bksv[DPRX_HDCP1X_BKSV_SIZE];
	__u8 bstatus[DPRX_HDCP1X_BSTATUS_SIZE];
	__u8 m0[DPRX_HDCP1X_M0_SIZE];
};

struct dprx_hdcp2x_info {
	enum v4l2_srccap_input_source source;
	enum dprx_hdcp2x_status status;

	__u8 rx_info[DPRX_HDCP2X_RX_INFO_SIZE];
	__u8 ake_rtx[DPRX_HDCP2X_AKE_RTX_SIZE];
	__u8 ake_tx_caps[DPRX_HDCP2X_AKE_TXCAPS_SIZE];
};

/* General */
int mtk_dprx_register_subdev(struct v4l2_device *v4l2_dev,
			     struct dprx_device *dev);

void mtk_dprx_unregister_subdev(struct dprx_device *dev);

int mtk_dprx_init(struct dprx_device *dev);
int mtk_dprx_deinit(struct dprx_device *dev);

int mtk_dprx_subscribe_event(struct dprx_device *dev,
			     const struct v4l2_event_subscription *event);

int mtk_dprx_unsubscribe_event(struct dprx_device *dev,
			       const struct v4l2_event_subscription *event);

bool mtk_dprx_is_valid_input_port(struct dprx_device *dev,
				  enum v4l2_srccap_input_source input_port);

void mtk_dprx_set_test_mode(struct dprx_device *dev, bool test_mode);

bool mtk_dprx_get_test_mode(struct dprx_device *dev);

void mtk_dprx_set_ktest_mode(struct dprx_device *dev, bool test_mode);

int mtk_dprx_input_to_dprx_id(struct dprx_device *dev,
			      enum v4l2_srccap_input_source input_port);

enum v4l2_srccap_input_source mtk_dprx_id_to_input_port(unsigned int port_id);

/* MUX */
int mtk_dprx_mux_set_active_port(struct dprx_device *dev,
				 enum v4l2_srccap_input_source port);

/* DSC */
int mtk_dprx_dsc_enable(struct dprx_device *dev,
			enum v4l2_srccap_input_source port,
			enum dprx_dsc_hw_mux dsc_hw_select,
			bool enable);

int mtk_dprx_dsc_get_output_crc(struct dprx_device *dev,
				enum v4l2_srccap_input_source port,
				u8 *crc);

int mtk_dprx_dsc_get_input_crc(struct dprx_device *dev,
			       enum dprx_dsc_hw_mux dsc_hw_select,
			       u16 *crc);

int mtk_dprx_dsc_get_pps(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port);


bool mtk_dprx_dsc_inputsignal_enable_dsc(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port);

/* MAC */
int mtk_dprx_set_power_state(struct dprx_device *dev, int power);

bool mtk_dprx_check_if_freesync(struct dprx_device *dev,
				enum v4l2_srccap_input_source port);

enum dprx_signal_status mtk_dprx_get_signal_status(struct dprx_device *dev,
		enum v4l2_srccap_input_source port);

u32 mtk_dprx_get_pixel_clock_10khz(struct dprx_device *dev,
				   enum v4l2_srccap_input_source port);

bool mtk_dprx_get_event_change(struct dprx_device *dev,
			       enum v4l2_srccap_input_source port);

bool mtk_dprx_get_h_polarity(struct dprx_device *dev,
			     enum v4l2_srccap_input_source port);

bool mtk_dprx_get_v_polarity(struct dprx_device *dev,
			     enum v4l2_srccap_input_source port);

enum dprx_version mtk_dprx_get_version(struct dprx_device *dev,
				       enum v4l2_srccap_input_source port);

int mtk_dprx_set_version(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port,
			 enum dprx_version version);

bool mtk_dprx_get_cable_connected(struct dprx_device *dev,
				  enum v4l2_srccap_input_source port);

int mtk_dprx_control_hpd(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port,
			 bool is_low);

bool mtk_dprx_get_hpd_status(struct dprx_device *dev,
			     enum v4l2_srccap_input_source port);

int mtk_dprx_get_hwcaps(struct dprx_device *dev,
			enum v4l2_srccap_input_source port,
			struct v4l2_dprx_hwcaps *hw_caps);

int mtk_dprx_get_lane_count(struct dprx_device *dev,
			    enum v4l2_srccap_input_source port);

int mtk_dprx_get_link_rate(struct dprx_device *dev,
			   enum v4l2_srccap_input_source port);

int mtk_dprx_get_timing_data(struct dprx_device *dev,
			     enum v4l2_srccap_input_source port,
			     u16 *timing_data);

enum dprx_colorimetry mtk_dprx_get_colorimetry(struct dprx_device *dev,
					enum v4l2_srccap_input_source port);

enum dprx_color_depth mtk_dprx_get_color_depth(struct dprx_device *dev,
					enum v4l2_srccap_input_source port);

enum dprx_color_space mtk_dprx_get_color_space(struct dprx_device *dev,
					enum v4l2_srccap_input_source port);

u32 mtk_dprx_get_pkt_status(struct dprx_device *dev,
			    enum v4l2_srccap_input_source port);

u8 mtk_dprx_get_vbid(struct dprx_device *dev,
		      enum v4l2_srccap_input_source port);

u32 mtk_dprx_get_framerate(struct dprx_device *dev,
			   enum v4l2_srccap_input_source port);

int mtk_dprx_get_msa(struct dprx_device *dev,
		     enum v4l2_srccap_input_source port,
		     struct v4l2_dprx_msa *msa);

u8 mtk_dprx_get_color_range(struct dprx_device *dev,
			    enum v4l2_srccap_input_source port);

int mtk_dprx_get_hw_detect_timing_info(struct dprx_device *dev,
				       enum v4l2_srccap_input_source port,
				       u16 *timing_info);

u16 mtk_dprx_get_hw_detect_htt(struct dprx_device *dev,
			       enum v4l2_srccap_input_source port);

u16 mtk_dprx_get_hw_detect_vtt(struct dprx_device *dev,
			       enum v4l2_srccap_input_source port);

u16 mtk_dprx_get_hw_detect_h_width(struct dprx_device *dev,
				   enum v4l2_srccap_input_source port);

u16 mtk_dprx_get_hw_detect_v_height(struct dprx_device *dev,
				    enum v4l2_srccap_input_source port);

void mtk_dprx_cable_detect_event(void *ptr);
void mtk_dprx_signal_change_event(void *ptr);
void mtk_dprx_polling_hw_event(void *ptr);
void mtk_dprx_timer_handler(struct timer_list *t);

/* PHY */
int mtk_dprx_phy_init(struct dprx_device *dev);
int mtk_dprx_phy_deinit(struct dprx_device *dev);

/* PKT RECV */
int mtk_dprx_pkt_get_raw_data_by_type(struct dprx_device *dev,
				      struct v4l2_dprx_pkt *pkt);

int mtk_dprx_pkt_get_avi(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port,
			 u8 length,
			 u8 *data);

int mtk_dprx_pkt_get_vsif(struct dprx_device *dev,
			  enum v4l2_srccap_input_source port,
			  u8 length,
			  u8 *data);

int mtk_dprx_pkt_get_hdrif(struct dprx_device *dev,
			   enum v4l2_srccap_input_source port,
			   u8 length,
			   u8 *data);

int mtk_dprx_pkt_get_pps(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port,
			 u8 length,
			 u8 *data);

int mtk_dprx_pkt_get_vsc(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port,
			 u8 length,
			 u8 *data);

int mtk_dprx_pkt_get_vsc_ext(void);

int mtk_dprx_pkt_get_adaptive_sync(struct dprx_device *dev,
				   enum v4l2_srccap_input_source port,
				   u8 length,
				   u8 *data);

int mtk_dprx_pkt_get_audio_stream(struct dprx_device *dev,
				  enum v4l2_srccap_input_source port,
				  u8 length,
				  u8 *data);

int mtk_dprx_pkt_get_audio_infoframe(struct dprx_device *dev,
				     enum v4l2_srccap_input_source port,
				     u8 length,
				     u8 *data);

int mtk_dprx_pkt_get_spd(struct dprx_device *dev,
			 enum v4l2_srccap_input_source port,
			 u8 length,
			 u8 *data);

int mtk_dprx_pkt_get_channel_status(struct dprx_device *dev,
				    enum v4l2_srccap_input_source port,
				    u8 length,
				    u8 *data);

bool mtk_dprx_pkt_is_audio_pcm(struct dprx_device *dev,
			       enum v4l2_srccap_input_source port);

bool mtk_dprx_pkt_get_no_audio_flag(struct dprx_device *dev,
				    enum v4l2_srccap_input_source port);

u8 mtk_dprx_pkt_get_audio_freq_by_ivs(struct dprx_device *dev,
				      enum v4l2_srccap_input_source port);

u8 mtk_dprx_pkt_get_audio_freq_by_mn(struct dprx_device *dev,
				     enum v4l2_srccap_input_source port);

u32 mtk_dprx_pkt_get_audio_m_value(struct dprx_device *dev,
				   enum v4l2_srccap_input_source port);

u32 mtk_dprx_pkt_get_audio_n_value(struct dprx_device *dev,
				   enum v4l2_srccap_input_source port);

bool mtk_dprx_pkt_check_audio_absent(struct dprx_device *dev,
				     enum v4l2_srccap_input_source port);

/* EDID */
int mtk_dprx_edid_write(struct dprx_device *dev,
			enum v4l2_srccap_input_source port,
			u8 *edid,
			u16 size);

int mtk_dprx_edid_read(struct dprx_device *dev,
		       enum v4l2_srccap_input_source port,
		       u8 *edid,
		       u16 size);

/* HDCP */
void mtk_dprx_hdcp_type_change_event(void *ptr);
void mtk_dprx_hdcp1x_status_change_event(void *ptr);
void mtk_dprx_hdcp2x_status_change_event(void *ptr);
enum dprx_hdcp_type mtk_dprx_hdcp_get_type(struct dprx_device *dev,
					   enum v4l2_srccap_input_source port);

#endif
