/*
 * SPDX-License-Identifier: MIT
 * Copyright (C) 2004 - 2022 AJA Video Systems, Inc.
 */
//==========================================================================
//
//  ntv2hdmiin4.c
//
//==========================================================================

#include "ntv2hdmiin4.h"
#include "ntv2hin4reg.h"
#include "ntv2infoframe.h"
#include "ntv2enums.h"

// debug messages
#define NTV2_DEBUG_INFO					0x00000001
#define NTV2_DEBUG_ERROR				0x00000002
#define NTV2_DEBUG_HDMIIN4_STATE		0x00000004
#define NTV2_DEBUG_HDMIIN4_DETECT		0x00000008
#define NTV2_DEBUG_HDMIIN4_AUX			0x00000010

#define NTV2_DEBUG_ACTIVE(msg_mask) \
	(((ntv2_debug_mask | ntv2_user_mask) & msg_mask) != 0)

#define NTV2_MSG_PRINT(msg_mask, string, ...) \
	if(NTV2_DEBUG_ACTIVE(msg_mask)) ntv2Message(string, __VA_ARGS__);

#define NTV2_MSG_INFO(string, ...)					NTV2_MSG_PRINT(NTV2_DEBUG_INFO, string, __VA_ARGS__)
#define NTV2_MSG_ERROR(string, ...)					NTV2_MSG_PRINT(NTV2_DEBUG_ERROR, string, __VA_ARGS__)
#define NTV2_MSG_HDMIIN4_INFO(string, ...)			NTV2_MSG_PRINT(NTV2_DEBUG_INFO, string, __VA_ARGS__)
#define NTV2_MSG_HDMIIN4_ERROR(string, ...)			NTV2_MSG_PRINT(NTV2_DEBUG_ERROR, string, __VA_ARGS__)
#define NTV2_MSG_HDMIIN4_STATE(string, ...)			NTV2_MSG_PRINT(NTV2_DEBUG_HDMIIN4_STATE, string, __VA_ARGS__)
#define NTV2_MSG_HDMIIN4_DETECT(string, ...)		NTV2_MSG_PRINT(NTV2_DEBUG_HDMIIN4_DETECT, string, __VA_ARGS__)
#define NTV2_MSG_HDMIIN4_AUX(string, ...)			NTV2_MSG_PRINT(NTV2_DEBUG_HDMIIN4_AUX, string, __VA_ARGS__)

static uint32_t ntv2_debug_mask = NTV2_DEBUG_INFO | NTV2_DEBUG_ERROR | NTV2_DEBUG_HDMIIN4_STATE | NTV2_DEBUG_HDMIIN4_DETECT;
static uint32_t ntv2_user_mask = 0;

enum ntv2_hdmi_clock_type
{
	ntv2_clock_type_unknown,
	ntv2_clock_type_sdd,
	ntv2_clock_type_sdn,
	ntv2_clock_type_hdd,
	ntv2_clock_type_hdn,
	ntv2_clock_type_3gd,
	ntv2_clock_type_3gn,
	ntv2_clock_type_4kd,
	ntv2_clock_type_4kn,
	ntv2_clock_type_h2d,
	ntv2_clock_type_h2n,
	ntv2_clock_type_size
};

struct ntv2_hdmi_format_data
{
	uint32_t					video_standard;
	uint32_t					frame_rate;
	uint32_t					h_sync_start;
	uint32_t					h_sync_end;
	uint32_t					h_de_start;
	uint32_t					h_total;
	uint32_t					v_trans_f1;
	uint32_t					v_trans_f2;
	uint32_t					v_sync_start_f1;
	uint32_t					v_sync_end_f1;
	uint32_t					v_de_start_f1;
	uint32_t					v_de_start_f2;
	uint32_t					v_sync_start_f2;
	uint32_t					v_sync_end_f2;
	uint32_t					v_total_f1;
	uint32_t					v_total_f2;
	uint32_t					clock_ratio;
	uint8_t						avi_byte_4;
	uint8_t						hdmi_byte_5;
	enum ntv2_hdmi_clock_type	clock_type;
};

struct ntv2_hdmi_clock_data
{
	enum ntv2_hdmi_clock_type	clock_type;
	uint32_t					bit_depth;
	uint32_t					line_rate;
	uint32_t					tmds_rate;
};


static struct ntv2_hdmi_format_data c_hdmi_format_data[] = {
	{ ntv2_video_standard_525i,	       ntv2_frame_rate_2997,    19,   81,  138,  858,   19,  448,    4,    7,   22,  285,  266,  269,  262,  525,    2,    0,    0, ntv2_clock_type_sdn },
	{ ntv2_video_standard_625i,	       ntv2_frame_rate_2500,    12,   75,  144,  864,   12,  444,    2,    5,   24,  337,  314,  317,  312,  625,    2,    0,    0, ntv2_clock_type_sdn },
	{ ntv2_video_standard_720p,	       ntv2_frame_rate_5000,   440,  480,  700, 1980,  440,    0,    5,   10,   30,    0,    0,    0,  750,    0,    2, 0x13,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_720p,	       ntv2_frame_rate_5994,   110,  150,  370, 1650,  110,    0,    5,   10,   30,    0,    0,    0,  750,    0,    2, 0x04,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_720p,	       ntv2_frame_rate_6000,   110,  150,  370, 1650,  110,    0,    5,   10,   30,    0,    0,    0,  750,    0,    2, 0x04,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_1080i,       ntv2_frame_rate_2500,   528,  572,  720, 2640,  528, 1848,    2,    7,   22,  585,  564,  569,  562, 1125,    2, 0x14,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_1080i,       ntv2_frame_rate_2997,    88,  132,  280, 2200,   88, 1188,    2,    7,   22,  585,  564,  569,  562, 1125,    2, 0x05,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_1080i,       ntv2_frame_rate_3000,    88,  132,  280, 2200,   88, 1188,    2,    7,   22,  585,  564,  569,  562, 1125,    2, 0x05,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_2398,   638,  682,  830, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2, 0x20,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_2400,   638,  682,  830, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2, 0x20,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_2500,   528,  572,  720, 2640,  528,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2, 0x21,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_2997,    88,  132,  280, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2, 0x22,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_3000,    88,  132,  280, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2, 0x22,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_4795,   638,  682,  830, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1, 0x6f,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_4800,   638,  682,  830, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1, 0x6f,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_5000,   528,  572,  720, 2640,  528,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1, 0x1f,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_5994,    88,  132,  280, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1, 0x10,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_1080p,       ntv2_frame_rate_6000,    88,  132,  280, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1, 0x10,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_2048x1080i,  ntv2_frame_rate_2500,   528,  572,  702, 2750,  528,    0,    2,    7,   22,  585,  564,  569,  562, 1125,    2,    0,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_2048x1080i,  ntv2_frame_rate_2997,    88,  132,  152, 2200,   88, 1188,    2,    7,   22,  585,  564,  569,  562, 1125,    2,    0,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_2048x1080i,  ntv2_frame_rate_3000,    88,  132,  152, 2200,   88, 1188,    2,    7,   22,  585,  564,  569,  562, 1125,    2,    0,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_2398,   638,  682,  702, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2,    0,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_2400,   638,  682,  702, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2,    0,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_2500,   528,  572,  592, 2640,  528,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2,    0,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_2997,    88,  132,  152, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2,    0,    0, ntv2_clock_type_hdd },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_3000,    88,  132,  152, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    2,    0,    0, ntv2_clock_type_hdn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_4795,   638,  682,  702, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1,    0,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_4800,   638,  682,  702, 2750,  638,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1,    0,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_5000,   528,  572,  592, 2640,  528,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1,    0,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_5000,   484,  528,  592, 2640,  484,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1,    0,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_5994,    88,  132,  152, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1,    0,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_2048x1080p,  ntv2_frame_rate_6000,    88,  132,  152, 2200,   88,    0,    4,    9,   45,    0,    0,    0, 1125,    0,    1,    0,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2398,   638,  682,  830, 2750,  638,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5d,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2400,   638,  682,  830, 2750,  638,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5d,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2500,   528,  572,  720, 2640,  528,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x60,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2997,    88,  132,  280, 2200,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x61,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_3000,    88,  132,  280, 2200,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x61,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2398,  1276, 1364, 1660, 5500, 1276,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5d, 0x03, ntv2_clock_type_4kd },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2400,  1276, 1364, 1660, 5500, 1276,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5d, 0x03, ntv2_clock_type_4kn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2500,  1056, 1144, 1440, 5280, 1056,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5e, 0x02, ntv2_clock_type_4kn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_2997,   176,  264,  560, 4400,  176,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5f, 0x01, ntv2_clock_type_4kd },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_3000,   176,  264,  560, 4400,  176,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x5f, 0x01, ntv2_clock_type_4kn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_4795,   638,  682,  830, 2750,  638,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x72,    0, ntv2_clock_type_4kd },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_4800,   638,  682,  830, 2750,  638,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x72,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_5000,   528,  572,  720, 2640,  528,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x60,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_5994,    88,  132,  280, 2200,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x61,    0, ntv2_clock_type_4kd },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_6000,    88,  132,  280, 2200,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x61,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_4795,  1276, 1364, 1660, 5500, 1276,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x72,    0, ntv2_clock_type_h2d },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_4800,  1276, 1364, 1660, 5500, 1276,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x72,    0, ntv2_clock_type_h2n },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_5000,  1056, 1144, 1440, 5280, 1056,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x60,    0, ntv2_clock_type_h2n },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_5994,   176,  264,  560, 4400,  176,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x61,    0, ntv2_clock_type_h2d },
	{ ntv2_video_standard_3840x2160p,  ntv2_frame_rate_6000,   176,  264,  560, 4400,  176,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x61,    0, ntv2_clock_type_h2n },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2398,   510,  554,  702, 2750,  510,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x62,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2400,   510,  554,  702, 2750,  510,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x62,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2500,   484,  528,  592, 2640,  484,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x63,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2997,    44,   88,  152, 2200,   44,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x64,    0, ntv2_clock_type_3gd },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_3000,    44,   88,  152, 2200,   44,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x64,    0, ntv2_clock_type_3gn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2398,  1020, 1108, 1404, 5500, 1020,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x62, 0x04, ntv2_clock_type_4kd },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2400,  1020, 1108, 1404, 5500, 1020,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x62, 0x04, ntv2_clock_type_4kn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2500,   968, 1056, 1184, 5280,  968,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x63,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_2997,    88,  176,  304, 4400,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x64,    0, ntv2_clock_type_4kd },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_3000,    88,  176,  304, 4400,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x64,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_4795,   510,  554,  702, 2750,  510,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x73,    0, ntv2_clock_type_4kd },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_4800,   510,  554,  702, 2750,  510,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x73,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_5000,   484,  528,  592, 2640,  484,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x65,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_5994,    44,   88,  152, 2200,   44,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x66,    0, ntv2_clock_type_4kd },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_6000,    44,   88,  152, 2200,   44,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x66,    0, ntv2_clock_type_4kn },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_4795,  1020, 1108, 1404, 5500, 1020,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x73,    0, ntv2_clock_type_h2d },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_4800,  1020, 1108, 1404, 5500, 1020,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x73,    0, ntv2_clock_type_h2n },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_5000,   968, 1056, 1184, 5280,  968,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x65,    0, ntv2_clock_type_h2n },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_5994,    88,  176,  304, 4400,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x66,    0, ntv2_clock_type_h2d },
	{ ntv2_video_standard_4096x2160p,  ntv2_frame_rate_6000,    88,  176,  304, 4400,   88,    0,    8,   18,   90,    0,    0,    0, 2250,    0,    0, 0x66,    0, ntv2_clock_type_h2n },
	{ ntv2_video_standard_none,        ntv2_frame_rate_none,     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, ntv2_clock_type_unknown }
};


static struct ntv2_hdmi_clock_data c_hdmi_clock_data[] = {
	{ ntv2_clock_type_sdd,     8,      ntv2_con_hdmiin4_linerate_270mhz,       26973027 },
	{ ntv2_clock_type_sdd,    10,      ntv2_con_hdmiin4_linerate_337mhz,       33716284 },
	{ ntv2_clock_type_sdd,    12,      ntv2_con_hdmiin4_linerate_405mhz,       40459540 },

	{ ntv2_clock_type_sdn,     8,      ntv2_con_hdmiin4_linerate_270mhz,       27000000 },
	{ ntv2_clock_type_sdn,    10,      ntv2_con_hdmiin4_linerate_337mhz,       33750000 },
	{ ntv2_clock_type_sdn,    12,      ntv2_con_hdmiin4_linerate_405mhz,       40500000 },

	{ ntv2_clock_type_hdd,     8,      ntv2_con_hdmiin4_linerate_742mhz,       74175824 },
	{ ntv2_clock_type_hdd,    10,      ntv2_con_hdmiin4_linerate_928mhz,       92719780 },
	{ ntv2_clock_type_hdd,    12,      ntv2_con_hdmiin4_linerate_1113mhz,     111263736 },

	{ ntv2_clock_type_hdn,     8,      ntv2_con_hdmiin4_linerate_742mhz,       74250000 },
	{ ntv2_clock_type_hdn,    10,      ntv2_con_hdmiin4_linerate_928mhz,       92812500 },
	{ ntv2_clock_type_hdn,    12,      ntv2_con_hdmiin4_linerate_1113mhz,     111375000 },

	{ ntv2_clock_type_3gd,     8,      ntv2_con_hdmiin4_linerate_1485mhz,     148351648 },
	{ ntv2_clock_type_3gd,    10,      ntv2_con_hdmiin4_linerate_1856mhz,     185439560 },
	{ ntv2_clock_type_3gd,    12,      ntv2_con_hdmiin4_linerate_2227mhz,     222527472 },

	{ ntv2_clock_type_3gn,     8,      ntv2_con_hdmiin4_linerate_1485mhz,     148500000 },
	{ ntv2_clock_type_3gn,    10,      ntv2_con_hdmiin4_linerate_1856mhz,     185625000 },
	{ ntv2_clock_type_3gn,    12,      ntv2_con_hdmiin4_linerate_2227mhz,     222750000 },

	{ ntv2_clock_type_4kd,     8,      ntv2_con_hdmiin4_linerate_2970mhz,     296703297 },
	{ ntv2_clock_type_4kn,     8,      ntv2_con_hdmiin4_linerate_2970mhz,     297000000 },

	{ ntv2_clock_type_4kd,    10,      ntv2_con_hdmiin4_linerate_3712mhz,      92719780 },
	{ ntv2_clock_type_4kd,    12,      ntv2_con_hdmiin4_linerate_4455mhz,     111263736 },
	{ ntv2_clock_type_4kn,    10,      ntv2_con_hdmiin4_linerate_3712mhz,      92812500 },
	{ ntv2_clock_type_4kn,    12,      ntv2_con_hdmiin4_linerate_4455mhz,     111375000 },

	{ ntv2_clock_type_h2d,     8,      ntv2_con_hdmiin4_linerate_5940mhz,     148351648 },
	{ ntv2_clock_type_h2n,     8,      ntv2_con_hdmiin4_linerate_5940mhz,     148500000 },

	{ ntv2_clock_type_unknown, 0,       0,                                    0 }
};


static const int64_t c_default_timeout		= 250000;
static const int64_t c_redriver_time		= 10000;
static const int64_t c_plug_time			= 250000;
static const int64_t c_aux_time				= 100000;
static const uint32_t c_lock_wait_max		= 2;
static const uint32_t c_unlock_wait_max		= 4;
static const uint32_t c_plug_wait_max		= 32;


static void ntv2_hdmiin4_monitor(void* data);
static Ntv2Status ntv2_hdmiin4_initialize(struct ntv2_hdmiin4 *ntv2_hin);

static bool is_input_present(struct ntv2_hdmiin4 *ntv2_hin);
static bool is_input_locked(struct ntv2_hdmiin4 *ntv2_hin);
//static bool is_deserializer_locked(struct ntv2_hdmiin4 *ntv2_hin);
static void reset_lock(struct ntv2_hdmiin4 *ntv2_hin);
static void hot_plug(struct ntv2_hdmiin4 *ntv2_hin);
static bool has_video_input_changed(struct ntv2_hdmiin4 *ntv2_hin);
static bool has_audio_input_changed(struct ntv2_hdmiin4 *ntv2_hin);
static bool update_input_state(struct ntv2_hdmiin4 *ntv2_hin);
static bool has_audio_control_changed(struct ntv2_hdmiin4 *ntv2_hin);
static bool config_audio_control(struct ntv2_hdmiin4 *ntv2_hin);
static void config_aux_data(struct ntv2_hdmiin4 *ntv2_hin);
static void set_no_video(struct ntv2_hdmiin4 *ntv2_hin);
static bool edid_write(struct ntv2_hdmiin4 *ntv2_hin, struct ntv2_hdmiedid* edid);
static bool edid_write_data(struct ntv2_hdmiin4 *ntv2_hin, uint8_t address, uint8_t data);
static bool edid_read_data(struct ntv2_hdmiin4 *ntv2_hin, uint8_t address, uint8_t* data);
static bool edid_wait_not_busy(struct ntv2_hdmiin4 *ntv2_hin);
static void aux_init(struct ntv2_hdmiin4 *ntv2_hin);
static int aux_read_ready(struct ntv2_hdmiin4 *ntv2_hin);
static void aux_read_done(struct ntv2_hdmiin4 *ntv2_hin);
static void aux_read(struct ntv2_hdmiin4 *ntv2_hin, int index, uint32_t *aux_data);
static int aux_find(struct ntv2_hdmiin4 *ntv2_hin, int count, uint8_t aux_type);
static void update_debug_flags(struct ntv2_hdmiin4 *ntv2_hin);

static struct ntv2_hdmi_format_data* find_format_data(uint32_t h_sync_start,
													 uint32_t h_sync_end,
													 uint32_t h_de_start,
													 uint32_t h_total,
													 uint32_t v_trans_f1,
													 uint32_t v_trans_f2,
													 uint32_t v_sync_start_f1,
													 uint32_t v_sync_end_f1,
													 uint32_t v_de_start_f1,
													 uint32_t v_de_start_f2,
													 uint32_t v_sync_start_f2,
													 uint32_t v_sync_end_f2,
													 uint32_t v_total_f1,
													 uint32_t v_total_f2,
													  enum ntv2_hdmi_clock_type	clockType,
													  uint32_t h_tol,
													  uint32_t v_tol);
static struct ntv2_hdmi_clock_data* find_clock_data(uint32_t lineRate, uint32_t tmdsRate);

struct ntv2_hdmiin4 *ntv2_hdmiin4_open(Ntv2SystemContext* sys_con,
									   const char *name, int index)
{
	struct ntv2_hdmiin4 *ntv2_hin = NULL;

	if ((sys_con == NULL) ||
		(name == NULL))
		return NULL;

	ntv2_hin = (struct ntv2_hdmiin4 *)ntv2MemoryAlloc(sizeof(struct ntv2_hdmiin4));
	if (ntv2_hin == NULL) {
		NTV2_MSG_ERROR("%s: ntv2_hdmiin4 instance memory allocation failed\n", name);
		return NULL;
	}
	memset(ntv2_hin, 0, sizeof(struct ntv2_hdmiin4));

	ntv2_hin->index = index;
#if defined(MSWindows)
	sprintf(ntv2_hin->name, "%s%d", name, index);
#else
	snprintf(ntv2_hin->name, NTV2_HDMIIN4_STRING_SIZE, "%s%d", name, index);
#endif
	ntv2_hin->system_context = sys_con;

	ntv2SpinLockOpen(&ntv2_hin->state_lock, sys_con);
	ntv2ThreadOpen(&ntv2_hin->monitor_task, sys_con, "hdmi4 input monitor");
	ntv2EventOpen(&ntv2_hin->monitor_event, sys_con);

	NTV2_MSG_HDMIIN4_INFO("%s: open ntv2_hdmiin4\n", ntv2_hin->name);

	return ntv2_hin;
}

void ntv2_hdmiin4_close(struct ntv2_hdmiin4 *ntv2_hin)
{
	if (ntv2_hin == NULL) 
		return;

	NTV2_MSG_HDMIIN4_INFO("%s: close ntv2_hdmiin4\n", ntv2_hin->name);

	ntv2_hdmiin4_disable(ntv2_hin);

	ntv2EventClose(&ntv2_hin->monitor_event);
	ntv2ThreadClose(&ntv2_hin->monitor_task);
	ntv2SpinLockClose(&ntv2_hin->state_lock);
	ntv2_hdmiedid_close(ntv2_hin->edid);

	memset(ntv2_hin, 0, sizeof(struct ntv2_hdmiin4));
	ntv2MemoryFree(ntv2_hin, sizeof(struct ntv2_hdmiin4));
}

Ntv2Status ntv2_hdmiin4_configure(struct ntv2_hdmiin4 *ntv2_hin,
								  enum ntv2_edid_type edid_type,
								  int port_index)
{
	Ntv2Status result = NTV2_STATUS_SUCCESS;

	if (ntv2_hin == NULL)
		return NTV2_STATUS_BAD_PARAMETER;

	NTV2_MSG_HDMIIN4_INFO("%s: configure hdmi input device\n", ntv2_hin->name);

	// configure edid
	if (edid_type != ntv2_edid_type_unknown) {
		ntv2_hin->edid = ntv2_hdmiedid_open(ntv2_hin->system_context, "edid", 0); 
		if (ntv2_hin->edid != NULL) {
			result = ntv2_hdmiedid_configure(ntv2_hin->edid, edid_type, port_index);
			if (result != NTV2_STATUS_SUCCESS) {
				ntv2_hdmiedid_close(ntv2_hin->edid);
				ntv2_hin->edid = NULL;
				NTV2_MSG_HDMIIN4_ERROR("%s: *error* configure edid failed\n", ntv2_hin->name);
			}
		} else {
			NTV2_MSG_HDMIIN4_ERROR("%s: *error* open edid failed\n", ntv2_hin->name);
		}
	}

	return NTV2_STATUS_SUCCESS;
}

Ntv2Status ntv2_hdmiin4_enable(struct ntv2_hdmiin4 *ntv2_hin)
{
	bool success ;

	if (ntv2_hin == NULL)
		return NTV2_STATUS_BAD_PARAMETER;

	if (ntv2_hin->monitor_enable)
		return NTV2_STATUS_SUCCESS;

	NTV2_MSG_HDMIIN4_STATE("%s: enable hdmi input monitor\n", ntv2_hin->name);

	ntv2EventClear(&ntv2_hin->monitor_event);
	ntv2_hin->monitor_enable = true;

	success = ntv2ThreadRun(&ntv2_hin->monitor_task, ntv2_hdmiin4_monitor, (void*)ntv2_hin);
	if (!success) {
		return NTV2_STATUS_FAIL;
	}

	return NTV2_STATUS_SUCCESS;
}

Ntv2Status ntv2_hdmiin4_disable(struct ntv2_hdmiin4 *ntv2_hin)
{
	if (ntv2_hin == NULL)
		return NTV2_STATUS_BAD_PARAMETER;

	if (!ntv2_hin->monitor_enable)
		return NTV2_STATUS_SUCCESS;

	NTV2_MSG_HDMIIN4_STATE("%s: disable hdmi input monitor\n", ntv2_hin->name);

	ntv2_hin->monitor_enable = false;
	ntv2EventSignal(&ntv2_hin->monitor_event);

	ntv2ThreadStop(&ntv2_hin->monitor_task);

	return NTV2_STATUS_SUCCESS;
}

static void ntv2_hdmiin4_monitor(void* data)
{
	struct ntv2_hdmiin4 *ntv2_hin = (struct ntv2_hdmiin4 *)data;
	uint32_t lock_wait = 0;
	uint32_t unlock_wait = 0;
	uint32_t plug_wait = 0;
	uint32_t val = 0;
	bool lock = false;
	bool reset = false;
	bool input = false;
	bool new_input = true;

	if (ntv2_hin == NULL)
		return;

	NTV2_MSG_HDMIIN4_STATE("%s: hdmi input monitor task start\n", ntv2_hin->name);

	ntv2_hdmiin4_initialize(ntv2_hin);

	while (!ntv2ThreadShouldStop(&ntv2_hin->monitor_task) && ntv2_hin->monitor_enable) 
	{
		val = ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmi_control, 0);
		if ((val & NTV2_FLD_MASK(ntv2_fld_hdmi_disable_update)) != 0)
		{
			goto wait;
		}

		update_debug_flags(ntv2_hin);

		if (is_input_present(ntv2_hin)) {
			if (!input) {
				NTV2_MSG_HDMIIN4_STATE("%s: input present\n", ntv2_hin->name);
				lock_wait = 0;
				plug_wait = 0;
			}
			input = true;
		} else {
			if (input) {
				NTV2_MSG_HDMIIN4_STATE("%s: input absent\n", ntv2_hin->name);
				unlock_wait = c_lock_wait_max;
				plug_wait = 0;
			}
			input = false;
		}

		if (input && is_input_locked(ntv2_hin)) {
			reset = false;
			unlock_wait = 0;

			lock_wait++;
			if (lock_wait < c_lock_wait_max) {
				goto wait;
			}

			if (!lock) {
				NTV2_MSG_HDMIIN4_STATE("%s: input locked\n", ntv2_hin->name);
				lock = true;
				plug_wait = 0;
			}

			if (has_video_input_changed(ntv2_hin) || has_audio_input_changed(ntv2_hin)) {
				NTV2_MSG_HDMIIN4_STATE("%s: input change detected\n", ntv2_hin->name);
				new_input = true;
			}

			if (has_audio_control_changed(ntv2_hin)) {
				NTV2_MSG_HDMIIN4_STATE("%s: audio control change detected\n", ntv2_hin->name);
				config_audio_control(ntv2_hin);
			}

			if (ntv2_hin->video_standard == ntv2_video_standard_none) {
				new_input = true;
			}

			if (new_input) {
				if (!update_input_state(ntv2_hin)) {
					plug_wait++;
					if (plug_wait > c_plug_wait_max) {
						NTV2_MSG_HDMIIN4_STATE("%s: bad input state (hot plug)\n", ntv2_hin->name);
						plug_wait = 0;
//						hot_plug(ntv2_hin);
					}
				}
				new_input = false;
			}

			config_aux_data(ntv2_hin);
		} 
		else {
			lock_wait = 0;

			unlock_wait++;
			if (unlock_wait < c_unlock_wait_max) {
				goto wait;
			}

			if (lock) {
				NTV2_MSG_HDMIIN4_STATE("%s: input unlocked\n", ntv2_hin->name);
				lock = false;
				plug_wait = 0;
			}

			if (!reset) {
				set_no_video(ntv2_hin);
				reset_lock(ntv2_hin);
				reset = true;
			}

			if (input) {
				plug_wait++;
				if (plug_wait > c_plug_wait_max) {
					NTV2_MSG_HDMIIN4_STATE("%s: lock timeout (hot plug)\n", ntv2_hin->name);
					plug_wait = 0;
//					hot_plug(ntv2_hin);
				}
			}
		}

	wait:
		// sleep
		ntv2EventWaitForSignal(&ntv2_hin->monitor_event, c_default_timeout, true);
	}

	NTV2_MSG_HDMIIN4_STATE("%s: hdmi input monitor task stop\n", ntv2_hin->name);
	ntv2ThreadExit(&ntv2_hin->monitor_task);
	return;
}

static Ntv2Status ntv2_hdmiin4_initialize(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t value;
	uint32_t mask;

	if (ntv2_hin == NULL)
		return NTV2_STATUS_BAD_PARAMETER;

	ntv2_hin->horizontal_tol	= 10;
	ntv2_hin->vertical_tol		= 4;

	ntv2_hin->video_control		= 0;
	ntv2_hin->video_detect0		= 0;
	ntv2_hin->video_detect1		= 0;
	ntv2_hin->video_detect2		= 0;
	ntv2_hin->video_detect3		= 0;
	ntv2_hin->video_detect4		= 0;
	ntv2_hin->video_detect5		= 0;
	ntv2_hin->video_detect6		= 0;
	ntv2_hin->video_detect7		= 0;
	ntv2_hin->tmds_rate			= 0;

	ntv2_hin->input_locked		= false;
	ntv2_hin->hdmi_mode			= false;
	ntv2_hin->video_standard	= ntv2_video_standard_none;
	ntv2_hin->frame_rate		= ntv2_frame_rate_none;
	ntv2_hin->color_space		= ntv2_color_space_none;
	ntv2_hin->color_depth		= ntv2_color_depth_none;
	ntv2_hin->aspect_ratio		= ntv2_aspect_ratio_unknown;
	ntv2_hin->colorimetry		= ntv2_colorimetry_unknown;
	ntv2_hin->quantization		= ntv2_quantization_unknown;

	ntv2_hin->audio_swap		= true;
	ntv2_hin->audio_resample	= true;

	// write edid
	if (ntv2_hin->edid != NULL) {
		edid_write(ntv2_hin, ntv2_hin->edid);
	}

	// intialize aux reads
	aux_init(ntv2_hin);

	// configure hot plug and audio swap
	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_hotplugmode, ntv2_con_hdmiin4_hotplugmode_disable);
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hotplugmode);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_audioswapmode, ntv2_con_hdmiin4_audioswapmode_enable);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_audioswapmode);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_audioresamplemode, ntv2_con_hdmiin4_audioresamplemode_enable);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_audioresamplemode);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

	// setup redriver
	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_redrivercontrol_power, ntv2_con_hdmiin4_power_disable);
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_redrivercontrol_power);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_redrivercontrol_pinmode, ntv2_con_hdmiin4_pinmode_enable);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_redrivercontrol_pinmode);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_redrivercontrol, ntv2_hin->index, value, mask);

	// wait for redriver reset
	ntv2EventWaitForSignal(&ntv2_hin->monitor_event, c_redriver_time, true);

	// enable redriver
	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_redrivercontrol_power, ntv2_con_hdmiin4_power_enable);
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_redrivercontrol_power);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_redrivercontrol, ntv2_hin->index, value, mask);

	// hot plug
	hot_plug(ntv2_hin);
	
	return NTV2_STATUS_SUCCESS;
}

static bool is_input_present(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t mask;
	uint32_t value;
	bool present;

	// read 5v detect
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hdmi5vdetect);
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index);
	present = (value & mask) == mask;

	// write hot plug
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hotplugmode);
	value = present? ntv2_con_hdmiin4_hotplugmode_enable : ntv2_con_hdmiin4_hotplugmode_disable;
	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_hotplugmode, value);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

	return present;
}

static bool is_input_locked(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_inputlock);
	uint32_t value;

	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index);
	if ((value & mask) == mask) return true;

	return false;
}
#if 0
static bool is_deserializer_locked(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_deserializerlock);
	uint32_t value;

	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index);
	if ((value & mask) == mask) return true;

	return false;
}
#endif
static void reset_lock(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t reset = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_reset);

	// reset lock
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, reset, reset);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, 0, reset);
}

static void hot_plug(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t value = 0;
	uint32_t mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hotplugmode);

	// disable hot plug
	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_hotplugmode, ntv2_con_hdmiin4_hotplugmode_disable);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

	// wait for input
	ntv2EventWaitForSignal(&ntv2_hin->monitor_event, c_plug_time, true);

	// configure hot plug
	is_input_present(ntv2_hin);
}

static bool has_video_input_changed(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t value;
	bool changed = false;
	uint32_t control_mask = 
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_scrambledetect) |
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_descramblemode) |
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_scdcratedetect) |
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_scdcratemode) |
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_linerate) |
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_inputlock) |
		NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hdmi5vdetect);

	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index) & control_mask;
	if (value != ntv2_hin->video_control) {
		ntv2_hin->video_control = value;
		changed = true;
	}

	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect0, ntv2_hin->index);
	if (value != ntv2_hin->video_detect0) {
		ntv2_hin->video_detect0 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect1, ntv2_hin->index);
	if (value != ntv2_hin->video_detect1) {
		ntv2_hin->video_detect1 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect2, ntv2_hin->index);
	if (value != ntv2_hin->video_detect2) {
		ntv2_hin->video_detect2 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect3, ntv2_hin->index);
	if (value != ntv2_hin->video_detect3) {
		ntv2_hin->video_detect3 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect4, ntv2_hin->index);
	if (value != ntv2_hin->video_detect4) {
		ntv2_hin->video_detect4 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect5, ntv2_hin->index);
	if (value != ntv2_hin->video_detect5) {
		ntv2_hin->video_detect5 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect6, ntv2_hin->index);
	if (value != ntv2_hin->video_detect6) {
		ntv2_hin->video_detect6 = value;
		changed = true;
	}
	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_videodetect7, ntv2_hin->index);
	if (value != ntv2_hin->video_detect7) {
		ntv2_hin->video_detect7 = value;
		changed = true;
	}

	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_tmdsclockfrequency, ntv2_hin->index);
	value = (value < 50000000)? (value & 0xfffff000) : (value & 0xffffc000);
	if (value != ntv2_hin->tmds_rate) {
		ntv2_hin->tmds_rate = value;
		changed = true;
	}

	return changed;
}

static bool has_audio_input_changed(struct ntv2_hdmiin4 *ntv2_hin)
{
#if defined (MSWindows)
	UNREFERENCED_PARAMETER(ntv2_hin);
#endif

	return false;
}

bool update_input_state(struct ntv2_hdmiin4 *ntv2_hin)
{
	struct ntv2_hdmi_clock_data* clock_data;
	struct ntv2_hdmi_format_data* format_data;
	uint32_t value;
	uint32_t mask;
	uint32_t line_rate;
	uint32_t color_depth;
	uint32_t color_space;
	uint32_t interface;
	uint32_t h_sync_start;
	uint32_t h_sync_end;
	uint32_t h_de_start;
	uint32_t h_total;
	uint32_t v_trans_f1;
	uint32_t v_trans_f2;
	uint32_t v_sync_start_f1;
	uint32_t v_sync_end_f1;
	uint32_t v_de_start_f1;
	uint32_t v_de_start_f2;
	uint32_t v_sync_start_f2;
	uint32_t v_sync_end_f2;
	uint32_t v_total_f1;
	uint32_t v_total_f2;

	bool input_locked = false;
	bool hdmi_mode = false;
	uint32_t video_rgb = 0;
	uint32_t video_deep = 0;
	uint32_t video_standard = 0;
	uint32_t video_prog = 0;
	uint32_t video_sd = 0;
	uint32_t frame_rate = 0;

	// read hardware input state
	line_rate = NTV2_FLD_GET(ntv2_fld_hdmiin4_videocontrol_linerate, ntv2_hin->video_control);

	NTV2_MSG_HDMIIN4_DETECT("%s: clock  line %d  tmds %d\n", 
							ntv2_hin->name, line_rate, ntv2_hin->tmds_rate);

	// find clock rate type base on hardware data
	clock_data = find_clock_data(line_rate, ntv2_hin->tmds_rate);
	if (clock_data == NULL)	{
		if (ntv2_hin->format_clock_count < 1) {
			NTV2_MSG_HDMIIN4_STATE("%s: unrecognized hardware clock data\n", ntv2_hin->name);
		}
		ntv2_hin->format_clock_count++;
		set_no_video(ntv2_hin);
		return false;
	}
	ntv2_hin->format_clock_count = 0;

	color_space = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect0_colorspace, ntv2_hin->video_detect0);
	color_depth = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect0_colordepth, ntv2_hin->video_detect0);
	interface = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect0_interfacemode, ntv2_hin->video_detect0);

	h_sync_start = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect1_hsyncstart, ntv2_hin->video_detect1);
	h_sync_end = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect1_hsyncend, ntv2_hin->video_detect1);
	
	h_de_start = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect2_hdestart, ntv2_hin->video_detect2);
	h_total = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect2_htotal, ntv2_hin->video_detect2);

	v_trans_f1 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect3_vtransf1, ntv2_hin->video_detect3);
	v_trans_f2 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect3_vtransf2, ntv2_hin->video_detect3);

	v_sync_start_f1 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect4_vsyncstartf1, ntv2_hin->video_detect4);
	v_sync_end_f1 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect4_vsyncendf1, ntv2_hin->video_detect4);

	v_de_start_f1 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect5_vdestartf1, ntv2_hin->video_detect5);
	v_de_start_f2 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect5_vdestartf2, ntv2_hin->video_detect5);

	v_sync_start_f2 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect6_vsyncstartf2, ntv2_hin->video_detect6);
	v_sync_end_f2 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect6_vsyncendf2, ntv2_hin->video_detect6);

	v_total_f1 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect7_vtotalf1, ntv2_hin->video_detect7);
	v_total_f2 = NTV2_FLD_GET(ntv2_fld_hdmiin4_videodetect7_vtotalf2, ntv2_hin->video_detect7);

	NTV2_MSG_HDMIIN4_DETECT("%s: detect  cs %d  cd %d  dvi %d\n", 
							ntv2_hin->name, color_space, color_depth, interface);
	NTV2_MSG_HDMIIN4_DETECT("%s: detect  hss %d  hse %d  hds %d  ht %d\n", 
							ntv2_hin->name, h_sync_start, h_sync_end, h_de_start, h_total);
	NTV2_MSG_HDMIIN4_DETECT("%s: detect  vtr1 %d  vtr2 %d  vss1 %d  vse1 %d\n", 
							ntv2_hin->name, v_trans_f1, v_trans_f2, v_sync_start_f1, v_sync_end_f1);
	NTV2_MSG_HDMIIN4_DETECT("%s: detect  vds1 %d  vds2 %d  vss2 %d  vse2 %d\n", 
							ntv2_hin->name, v_de_start_f1, v_de_start_f2, v_sync_start_f2, v_sync_end_f2);
	NTV2_MSG_HDMIIN4_DETECT("%s: detect  vtot1 %d  vtot2 %d\n", 
							ntv2_hin->name, v_total_f1, v_total_f2);

	// find the format based on the hardware registers
	format_data = find_format_data(h_sync_start,
								   h_sync_end,
								   h_de_start,
								   h_total,
								   v_trans_f1,
								   v_trans_f2,
								   v_sync_start_f1,
								   v_sync_end_f1,
								   v_de_start_f1,
								   v_de_start_f2,
								   v_sync_start_f2,
								   v_sync_end_f2,
								   v_total_f1,
								   v_total_f2,
								   clock_data->clock_type,
								   ntv2_hin->horizontal_tol,
								   ntv2_hin->vertical_tol);
	if (format_data == NULL) {
		if (ntv2_hin->format_raster_count < 1) {
			NTV2_MSG_HDMIIN4_STATE("%s: unrecognized hardware raster data\n", ntv2_hin->name);
		}
		ntv2_hin->format_raster_count++;
		set_no_video(ntv2_hin);
		return false;
	}
	ntv2_hin->format_raster_count = 0;

	// get video data
	input_locked = true;
	hdmi_mode = (interface == ntv2_con_hdmiin4_interfacemode_hdmi);
	video_standard = format_data->video_standard;
	frame_rate = format_data->frame_rate;
	video_prog = ntv2_video_standard_progressive(video_standard);
	video_rgb = (color_space == ntv2_color_space_rgb444)? 1 : 0;
	video_sd = ((video_standard == ntv2_video_standard_525i) || (video_standard == ntv2_video_standard_625i))? 1 : 0;
	video_deep = ((color_space == ntv2_color_space_yuv422) || 
				  ((color_space == ntv2_color_space_rgb444) && (color_depth != ntv2_color_depth_8bit)))? 1 : 0;

	// check to do 420 conversions
	if (color_space == ntv2_color_space_yuv420)
	{
		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_420mode, ntv2_con_hdmiin4_420mode_enable);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_420mode);
		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_420convert, ntv2_con_hdmiin4_420convert_enable);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_420convert);
		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_pixelcontrol, ntv2_hin->index, value, mask);
	}
	else
	{
		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_420mode, ntv2_con_hdmiin4_420mode_disable);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_420mode);
		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_420convert, ntv2_con_hdmiin4_420convert_disable);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_420convert);
		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_pixelcontrol, ntv2_hin->index, value, mask);
	}

	// check to do 4K conversions
	if (ntv2_video_standard_width(video_standard) > 2048)
	{
		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_hsyncdivide, ntv2_con_hdmiin4_hsyncdivide_none);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hsyncdivide);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_pixelsperclock, 4);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_pixelsperclock);

		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_hlinefilter, ntv2_con_hdmiin4_hlinefilter_disable);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_hlinefilter);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_clockratio, format_data->clock_ratio);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_clockratio);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_lineinterleave, ntv2_con_hdmiin4_lineinterleave_enable);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_lineinterleave);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_pixelinterleave, ntv2_con_hdmiin4_pixelinterleave_enable);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_pixelinterleave);

		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_pixelcontrol, ntv2_hin->index, value, mask);
	}
	else
	{
		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_hsyncdivide, ntv2_con_hdmiin4_hsyncdivide_none);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_hsyncdivide);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_pixelsperclock, 1);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_pixelsperclock);

		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_hlinefilter, ntv2_con_hdmiin4_hlinefilter_disable);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_hlinefilter);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_clockratio, format_data->clock_ratio);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_clockratio);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_lineinterleave, ntv2_con_hdmiin4_lineinterleave_disable);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_lineinterleave);

		value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_pixelinterleave, ntv2_con_hdmiin4_pixelinterleave_disable);
		mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_pixelinterleave);

		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_pixelcontrol, ntv2_hin->index, value, mask);
	}

	// disable crop
	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_croplocation_start, 0x040);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_croplocation_end, 0x7bf);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_croplocation, ntv2_hin->index, value);

	value = NTV2_FLD_SET(ntv2_fld_hdmiin4_pixelcontrol_cropmode, ntv2_con_hdmiin4_cropmode_disable);
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_pixelcontrol_cropmode);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_pixelcontrol, ntv2_hin->index, value, mask);

	// write input format
	value = NTV2_FLD_SET(ntv2_fld_hdmiin_locked, input_locked? 1 : 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_stable, input_locked? 1 : 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_rgb, video_rgb);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_deep_color, video_deep);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_code, video_standard);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_audio_2ch, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_progressive, video_prog);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_sd, video_sd);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_74_25, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_audio_rate, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_audio_word_length, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_format, video_standard);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_dvi, (hdmi_mode? 0 : 1));
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_rate, frame_rate);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin_input_status, ntv2_hin->index, value);

	value = NTV2_FLD_SET(ntv2_fld_hdmiin_color_space, color_space);
	if (color_space == ntv2_color_space_yuv422)
	{
		value |= NTV2_FLD_SET(ntv2_fld_hdmiin_color_depth, ntv2_color_depth_10bit);
	}
	else if (color_space == ntv2_color_space_yuv420)
	{
		value |= NTV2_FLD_SET(ntv2_fld_hdmiin_color_depth, ntv2_color_depth_8bit);
	}
	else
	{
		value |= NTV2_FLD_SET(ntv2_fld_hdmiin_color_depth, color_depth);
	}
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin_color_space);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin_color_depth);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmi_control, ntv2_hin->index, value, mask);

//	if ((ntv2_hin->input_locked != input_locked) ||
//		(ntv2_hin->hdmi_mode != hdmi_mode) ||
//		(ntv2_hin->video_standard != video_standard) ||
//		(ntv2_hin->frame_rate != frame_rate) ||
//		(ntv2_hin->color_space != color_space) ||
//		(ntv2_hin->color_depth != color_depth)) 
	{
		NTV2_MSG_HDMIIN4_STATE("%s: new format  mode %s  std %s  rate %s  clr %s  dpth %s\n",
							   ntv2_hin->name,
							   hdmi_mode? "hdmi" : "dvi",
							   ntv2_video_standard_name(video_standard),
							   ntv2_frame_rate_name(frame_rate),
							   ntv2_color_space_name(color_space),
							   ntv2_color_depth_name(color_depth));

		ntv2_hin->input_locked = input_locked;
		ntv2_hin->hdmi_mode = hdmi_mode;
		ntv2_hin->video_standard = video_standard;
		ntv2_hin->frame_rate = frame_rate;
		ntv2_hin->color_space = color_space;
		ntv2_hin->color_depth = color_depth;
	}

	return true;
}

static bool has_audio_control_changed(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t value = 0;
	bool changed = false;
	bool audio_swap;
	bool audio_resample;

	value =  ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmi_control, ntv2_hin->index);
	audio_swap = NTV2_FLD_GET(ntv2_fld_hdmiin_channel34_swap_disable, value) == 0;
	audio_resample = NTV2_FLD_GET(ntv2_fld_hdmiin_rate_convert_enable, value) == 0;

	if (audio_swap != ntv2_hin->audio_swap) {
		ntv2_hin->audio_swap = audio_swap;
		changed = true;
	}


	if (audio_resample != ntv2_hin->audio_resample) {
		ntv2_hin->audio_resample = audio_resample;
		changed = true;
	}

	return changed;
}

static bool config_audio_control(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t data = ntv2_hin->audio_swap? ntv2_con_hdmiin4_audioswapmode_enable : ntv2_con_hdmiin4_audioswapmode_disable;
	uint32_t value = NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_audioswapmode, data);
	uint32_t mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_audioswapmode);

	data = ntv2_hin->audio_resample? ntv2_con_hdmiin4_audioresamplemode_enable : ntv2_con_hdmiin4_audioresamplemode_disable;
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_videocontrol_audioresamplemode, data);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin4_videocontrol_audioresamplemode);

	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmiin4_videocontrol, ntv2_hin->index, value, mask);

	NTV2_MSG_HDMIIN4_STATE("%s: new control  audio swap %s  resample %s\n", ntv2_hin->name, 
						   ntv2_hin->audio_swap? "enable":"disable",
						   ntv2_hin->audio_resample? "enable":"disable");

	return true;
}
	
static void config_aux_data(struct ntv2_hdmiin4 *ntv2_hin)
{
	struct ntv2_avi_info_data avi_data;
	struct ntv2_drm_info_data drm_data;
	struct ntv2_vsp_info_data vsp_data;
	uint32_t aux_data[ntv2_con_auxdata_size];
	uint32_t full_range = 0;
	uint32_t value = 0;
	uint32_t mask = 0;
	uint32_t red_x = 0;
	uint32_t red_y = 0;
	uint32_t green_x = 0;
	uint32_t green_y = 0;
	uint32_t blue_x = 0;
	uint32_t blue_y = 0;
	int aux_count = 0;
	int aux_index = 0;
	bool found = false;

	if (!ntv2_hin->hdmi_mode)
		return;
	
	aux_count = aux_read_ready(ntv2_hin);
	if (aux_count == 0) {
		aux_init(ntv2_hin);
		return;
	}

	NTV2_MSG_HDMIIN4_AUX("%s: detect %d info frames\n", ntv2_hin->name, aux_count);

	// look for video infoframe
	found = false;
	aux_index = aux_find(ntv2_hin, aux_count, (uint8_t)ntv2_con_header_type_video_info);
	if (aux_index >= 0) {
		NTV2_MSG_HDMIIN4_AUX("%s: found avi info frame %d\n", ntv2_hin->name, aux_index);

		aux_read(ntv2_hin, aux_index, aux_data);

		NTV2_MSG_HDMIIN4_AUX("%s: read avi info: %08x %08x %08x %08x\n",
							 ntv2_hin->name, aux_data[0], aux_data[1], aux_data[2], aux_data[3]);
		NTV2_MSG_HDMIIN4_AUX("%s: read avi info: %08x %08x %08x %08x\n",
							 ntv2_hin->name, aux_data[4], aux_data[5], aux_data[6], aux_data[7]);
	
		found = ntv2_aux_to_avi_info(aux_data, ntv2_con_auxdata_size, &avi_data);
		if (found) {
			if (avi_data.quantization != ntv2_hin->quantization) {
				full_range = (avi_data.quantization == ntv2_quantization_full)? 1 : 0;
				if ((avi_data.quantization == ntv2_quantization_default) &&
					(avi_data.color_space == ntv2_color_space_rgb444)) {
					full_range = 1;
				}
				value = NTV2_FLD_SET(ntv2_fld_hdmiin_full_range, full_range);
				mask = NTV2_FLD_MASK(ntv2_fld_hdmiin_full_range);
				ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmi_control, ntv2_hin->index, value, mask);
				NTV2_MSG_HDMIIN4_DETECT("%s: detect  quant %s  range %s \n", 
										ntv2_hin->name,
										((avi_data.quantization == ntv2_quantization_default)? "default" :
										 ((avi_data.quantization == ntv2_quantization_full)? "full" : "limited")),
										((full_range == 1)? "full" : "smpte"));
				ntv2_hin->quantization = avi_data.quantization;
			}

			if (avi_data.colorimetry != ntv2_hin->colorimetry) {
				switch (avi_data.colorimetry)
				{
				case ntv2_colorimetry_170m:
				case ntv2_colorimetry_xvycc_601:
				case ntv2_colorimetry_adobe_601:
					value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetry601);
					break;
				case ntv2_colorimetry_bt709:
				case ntv2_colorimetry_xvycc_709:
				case ntv2_colorimetry_adobe_rgb:
					value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetry709);
					break;
				case ntv2_colorimetry_bt2020_cl:
					value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetry2020CL);
					break;
				case ntv2_colorimetry_bt2020:
					value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetry2020);
					break;
				case ntv2_colorimetry_dcip3_d65:
					value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetryDCI);
					break;
				default:
					value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetryNoData);
					break;
				}
				mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_colorimetry);
				ntv2_vreg_rmw(ntv2_hin->system_context, ntv2_vreg_hdmiin4_avi_info, ntv2_hin->index, value, mask);
				ntv2_hin->colorimetry = avi_data.colorimetry;
			}

			ntv2_hin->aspect_ratio = avi_data.aspect_ratio;
		}
	}

	if (!found) {
		value = NTV2_FLD_SET(ntv2_fld_hdmiin_full_range, 0);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin_full_range);
		ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmi_control, ntv2_hin->index, value, mask);

		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_colorimetry, NTV2_HDMIColorimetryNoData);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_colorimetry);
		ntv2_vreg_rmw(ntv2_hin->system_context, ntv2_vreg_hdmiin4_avi_info, ntv2_hin->index, value, mask);

		ntv2_hin->quantization = ntv2_quantization_unknown;
		ntv2_hin->colorimetry = ntv2_colorimetry_unknown;
		ntv2_hin->aspect_ratio = ntv2_aspect_ratio_unknown;
	}

	// look for video infoframe
	found = false;
	aux_index = aux_find(ntv2_hin, aux_count, (uint8_t)ntv2_con_header_type_vendor_specific);
	if (aux_index >= 0) {
		NTV2_MSG_HDMIIN4_AUX("%s: found vendor specific info frame %d\n", ntv2_hin->name, aux_index);

		aux_read(ntv2_hin, aux_index, aux_data);

		NTV2_MSG_HDMIIN4_AUX("%s: read vendor specific info: %08x %08x %08x %08x\n",
							 ntv2_hin->name, aux_data[0], aux_data[1], aux_data[2], aux_data[3]);
		NTV2_MSG_HDMIIN4_AUX("%s: read vendor specific info: %08x %08x %08x %08x\n",
							 ntv2_hin->name, aux_data[4], aux_data[5], aux_data[6], aux_data[7]);

		found = ntv2_aux_to_vsp_info(aux_data, ntv2_con_auxdata_size, &vsp_data);
		if (found) {
			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_dolby_vision, vsp_data.dolby_vision);
			mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_dolby_vision);
			ntv2_vreg_rmw(ntv2_hin->system_context, ntv2_vreg_hdmiin4_avi_info, ntv2_hin->index, value, mask);
		}
	}

	if (!found) {
		value = NTV2_FLD_SET(ntv2_fld_hdmiin4_dolby_vision, 0);
		mask = NTV2_FLD_MASK(ntv2_fld_hdmiin4_dolby_vision);
		ntv2_vreg_rmw(ntv2_hin->system_context, ntv2_vreg_hdmiin4_avi_info, ntv2_hin->index, value, mask);
	}

	// look for dynamic range mastering infoframe
	found = false;
	aux_index = aux_find(ntv2_hin, aux_count, (uint8_t)ntv2_con_header_type_drm_info);
	if (aux_index >= 0) {
		NTV2_MSG_HDMIIN4_AUX("%s: found drm info frame %d\n", ntv2_hin->name, aux_index);

		aux_read(ntv2_hin, aux_index, aux_data);

		NTV2_MSG_HDMIIN4_AUX("%s: read drm info: %08x %08x %08x %08x\n",
							 ntv2_hin->name, aux_data[0], aux_data[1], aux_data[2], aux_data[3]);
		NTV2_MSG_HDMIIN4_AUX("%s: read drm info: %08x %08x %08x %08x\n",
							 ntv2_hin->name, aux_data[4], aux_data[5], aux_data[6], aux_data[7]);

		found = ntv2_aux_to_drm_info(aux_data, ntv2_con_auxdata_size, &drm_data);
		if (found) {
			// drm info found
			if (drm_data.primary_x0 >= drm_data.primary_x1) {
				if (drm_data.primary_x0 >= drm_data.primary_x2) {
					red_x = drm_data.primary_x0;
					red_y = drm_data.primary_y0;
					if (drm_data.primary_y1 >= drm_data.primary_y2) {
						green_x = drm_data.primary_x1;
						green_y = drm_data.primary_y1;
						blue_x = drm_data.primary_x2;
						blue_y = drm_data.primary_y2;
					} else {
						green_x = drm_data.primary_x2;
						green_y = drm_data.primary_y2;
						blue_x = drm_data.primary_x1;
						blue_y = drm_data.primary_y1;
					}
				} else {
					red_x = drm_data.primary_x2;
					red_y = drm_data.primary_y2;
					if (drm_data.primary_y0 >= drm_data.primary_y1) {
						green_x = drm_data.primary_x0;
						green_y = drm_data.primary_y0;
						blue_x = drm_data.primary_x1;
						blue_y = drm_data.primary_y1;
					} else {
						green_x = drm_data.primary_x1;
						green_y = drm_data.primary_y1;
						blue_x = drm_data.primary_x0;
						blue_y = drm_data.primary_y0;
					}
				}
			} else {
				if (drm_data.primary_x1 >= drm_data.primary_x2) {
					red_x = drm_data.primary_x1;
					red_y = drm_data.primary_y1;
					if (drm_data.primary_y0 >= drm_data.primary_y2) {
						green_x = drm_data.primary_x0;
						green_y = drm_data.primary_y0;
						blue_x = drm_data.primary_x2;
						blue_y = drm_data.primary_y2;
					} else {
						green_x = drm_data.primary_x2;
						green_y = drm_data.primary_y2;
						blue_x = drm_data.primary_x0;
						blue_y = drm_data.primary_y0;
					}
				} else {
					red_x = drm_data.primary_x2;
					red_y = drm_data.primary_y2;
					if (drm_data.primary_y0 >= drm_data.primary_y1) {
						green_x = drm_data.primary_x0;
						green_y = drm_data.primary_y0;
						blue_x = drm_data.primary_x1;
						blue_y = drm_data.primary_y1;
					} else {
						green_x = drm_data.primary_x1;
						green_y = drm_data.primary_y1;
						blue_x = drm_data.primary_x0;
						blue_y = drm_data.primary_y0;
					}
				}
			}

			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_present, 1);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_eotf, drm_data.eotf);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_metadata_id, drm_data.metadata_id);						
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_info, ntv2_hin->index, value);
			
			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_red_x, red_x);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_red_y, red_y);
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_red, ntv2_hin->index, value);

			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_green_x, green_x);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_green_y, green_y);
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_green, ntv2_hin->index, value);

			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_blue_x, blue_x);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_blue_y, blue_y);
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_blue, ntv2_hin->index, value);

			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_white_x, drm_data.white_point_x);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_white_y, drm_data.white_point_y);
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_white, ntv2_hin->index, value);

			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_luma_max, drm_data.luminance_max);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_luma_min, drm_data.luminance_min);
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_luma, ntv2_hin->index, value);

			value = NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_light_content_max, drm_data.content_level_max);
			value |= NTV2_FLD_SET(ntv2_fld_hdmiin4_drm_light_average_max, drm_data.frameavr_level_max);
			ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_light, ntv2_hin->index, value);
		}
	}

	if (!found) {
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_info, ntv2_hin->index, 0);
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_red, ntv2_hin->index, 0);
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_green, ntv2_hin->index, 0);
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_blue, ntv2_hin->index, 0);
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_white, ntv2_hin->index, 0);
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_luma, ntv2_hin->index, 0);
		ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_light, ntv2_hin->index, 0);
	}

	aux_read_done(ntv2_hin);
}

static void set_no_video(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t value;
	uint32_t mask;

	// clear fpga hdmi status
	value = NTV2_FLD_SET(ntv2_fld_hdmiin_locked, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_stable, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_rgb, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_deep_color, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_code, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_audio_2ch, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_progressive, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_sd, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_74_25, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_audio_rate, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_audio_word_length, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_format, 0);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_dvi, 1);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_video_rate, 0);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin_input_status, ntv2_hin->index, value);

	value = NTV2_FLD_SET(ntv2_fld_hdmiin_color_space, 0);
	mask = NTV2_FLD_MASK(ntv2_fld_hdmiin_color_space);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_color_depth, 0);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin_color_depth);
	value |= NTV2_FLD_SET(ntv2_fld_hdmiin_full_range, 0);
	mask |= NTV2_FLD_MASK(ntv2_fld_hdmiin_full_range);
	ntv2_reg_rmw(ntv2_hin->system_context, ntv2_reg_hdmi_control, ntv2_hin->index, value, mask);

	ntv2_hin->input_locked		= false;
	ntv2_hin->hdmi_mode			= false;
	ntv2_hin->video_standard	= ntv2_video_standard_none;
	ntv2_hin->frame_rate		= ntv2_frame_rate_none;
	ntv2_hin->color_space		= ntv2_color_space_none;
	ntv2_hin->color_depth		= ntv2_color_depth_none;
	ntv2_hin->aspect_ratio		= ntv2_aspect_ratio_unknown;
	ntv2_hin->colorimetry		= ntv2_colorimetry_unknown;
	ntv2_hin->quantization		= ntv2_quantization_unknown;

	// no info
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_avi_info, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_info, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_red, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_green, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_blue, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_white, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_luma, ntv2_hin->index, 0);
	ntv2_vreg_write(ntv2_hin->system_context, ntv2_vreg_hdmiin4_drm_light, ntv2_hin->index, 0);
}

static struct ntv2_hdmi_format_data* find_format_data(uint32_t h_sync_start,
													  uint32_t h_sync_end,
													  uint32_t h_de_start,
													  uint32_t h_total,
													  uint32_t v_trans_f1,
													  uint32_t v_trans_f2,
													  uint32_t v_sync_start_f1,
													  uint32_t v_sync_end_f1,
													  uint32_t v_de_start_f1,
													  uint32_t v_de_start_f2,
													  uint32_t v_sync_start_f2,
													  uint32_t v_sync_end_f2,
													  uint32_t v_total_f1,
													  uint32_t v_total_f2,
													  enum ntv2_hdmi_clock_type clock_type,
													  uint32_t h_tol,
													  uint32_t v_tol)
{
	uint32_t delta = 1000000;
	int min = (-1);
	int i = 0;
	uint32_t dhd = ntv2_diff(h_total, h_de_start);
	uint32_t df1d = ntv2_diff(v_total_f1, v_de_start_f1);
	uint32_t df2d = ntv2_diff(v_total_f2, v_de_start_f2);

	(void)h_sync_start;
	(void)h_sync_end;
	(void)v_trans_f1;
	(void)v_trans_f2;
	(void)v_sync_start_f1;
	(void)v_sync_end_f1;
	(void)v_de_start_f1;
	(void)v_de_start_f2;
	(void)v_sync_start_f2;
	(void)v_sync_end_f2;
	
	while (c_hdmi_format_data[i].video_standard != ntv2_video_standard_none)
	{
		uint32_t dht = ntv2_diff(c_hdmi_format_data[i].h_total, c_hdmi_format_data[i].h_de_start);
		uint32_t df1t = ntv2_diff(c_hdmi_format_data[i].v_total_f1, c_hdmi_format_data[i].v_de_start_f1);
		uint32_t df2t = ntv2_diff(c_hdmi_format_data[i].v_total_f2, c_hdmi_format_data[i].v_de_start_f2);
		uint32_t dh = ntv2_diff(dhd, dht);
		uint32_t df1 = ntv2_diff(df1d, df1t);
		uint32_t df2 = ntv2_diff(df2d, df2t);
		uint32_t dl;

		if ((clock_type == c_hdmi_format_data[i].clock_type) &&
			(dh <= h_tol) && (df1 <= v_tol) && (df2 <= v_tol))
		{
			if ((h_total == c_hdmi_format_data[i].h_total) &&
				(v_total_f1 == c_hdmi_format_data[i].v_total_f1) &&
				(v_total_f2 == c_hdmi_format_data[i].v_total_f2))
			{
				return &c_hdmi_format_data[i];
			}

			if (c_hdmi_format_data[i].v_total_f2 != 0)
			{
				dl = ntv2_diff((h_total * v_total_f2), (c_hdmi_format_data[i].h_total * c_hdmi_format_data[i].v_total_f2));
			}
			else
			{
				dl = ntv2_diff((h_total * v_total_f1), (c_hdmi_format_data[i].h_total * c_hdmi_format_data[i].v_total_f1));
			}

			if (dl == 0)
				return &c_hdmi_format_data[i];

			if (dl < delta) {
				delta = dl;
			    min = i;
			}
		}
		i++;
	}

	if (min < 0)
		return NULL;

	return &c_hdmi_format_data[min];
}

static struct ntv2_hdmi_clock_data* find_clock_data(uint32_t lineRate, uint32_t tmdsRate)
{
	uint32_t diff;
//	uint32_t tol;
	uint32_t minDiff = 1000000000;
	int min = (-1);
	int i = 0;

	// find the clock 
	while (c_hdmi_clock_data[i].clock_type != ntv2_clock_type_unknown)
	{
		if (lineRate == c_hdmi_clock_data[i].line_rate) {
			diff = ntv2_diff(tmdsRate, c_hdmi_clock_data[i].tmds_rate);
			if (diff < minDiff)	{
				minDiff = diff;
				min = i;
			}
		}
		i++;
	}
	
	if (min < 0)
		return NULL;

	// check clock rate (+|- 0.5%)
#if 0	
	tol = c_hdmi_clock_data[min].tmds_rate / 200;
	if ((tmdsRate < (c_hdmi_clock_data[min].tmds_rate - tol)) ||
		(tmdsRate > (c_hdmi_clock_data[min].tmds_rate + tol)))
	{
	}
#endif
	return &c_hdmi_clock_data[min];
}

static bool edid_write(struct ntv2_hdmiin4 *ntv2_hin, struct ntv2_hdmiedid* ntv2_edid)
{
	uint8_t* data = ntv2_hdmi_get_edid_data(ntv2_edid);
	uint32_t count = ntv2_hdmi_get_edid_size(ntv2_edid);
	uint32_t address = 0;
	uint8_t value;
	
	for (address = 0; address < count; address++) {
		if (!edid_write_data(ntv2_hin, (uint8_t)address, data[address])) {
			NTV2_MSG_HDMIIN4_ERROR("%s: *error* write edid failed  address %02x\n",
								   ntv2_hin->name, address);
			return false;
		}
	}

	for (address = 0; address < count; address++) {
		if (!edid_read_data(ntv2_hin, (uint8_t)address, &value)) {
			NTV2_MSG_HDMIIN4_ERROR("%s: *error* read edid failed  address %02x\n",
								   ntv2_hin->name, address);
			return false;
		}
		if (value != data[address]) {
			NTV2_MSG_HDMIIN4_ERROR("%s: *error* verify edid failed  address %02x  exp %02x  act %02x\n",
								   ntv2_hin->name, address, data[address], value);
			return false;
		}
	}

	return true;
}

static bool edid_write_data(struct ntv2_hdmiin4 *ntv2_hin, uint8_t address, uint8_t data)
{
	uint32_t value;

	// wait for not busy
	if (!edid_wait_not_busy(ntv2_hin)) return false;

	// write edid data
	value = NTV2_FLD_SET(ntv2_kona_fld_hdmiin4_edid_address, address);
	value |= NTV2_FLD_SET(ntv2_kona_fld_hdmiin4_edid_write_data, data);
	value |= NTV2_FLD_SET(ntv2_kona_fld_hdmiin4_edid_write_enable, 1);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_kona_reg_hdmiin4_edid, ntv2_hin->index, value);

	return true;
}

static bool edid_read_data(struct ntv2_hdmiin4 *ntv2_hin, uint8_t address, uint8_t* data)
{
	uint32_t value;

	// wait for not busy
	if (!edid_wait_not_busy(ntv2_hin)) return false;

	// request edid read
	value = NTV2_FLD_SET(ntv2_kona_fld_hdmiin4_edid_address, address);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_kona_reg_hdmiin4_edid, ntv2_hin->index, value);

	// wait for read
	if (!edid_wait_not_busy(ntv2_hin)) return false;

	// read data
	value = ntv2_reg_read(ntv2_hin->system_context, ntv2_kona_reg_hdmiin4_edid, ntv2_hin->index);
	*data = (uint8_t)NTV2_FLD_GET(ntv2_kona_fld_hdmiin4_edid_read_data, value);

	return true;
}

static bool edid_wait_not_busy(struct ntv2_hdmiin4 *ntv2_hin)
{
	int i;
	uint32_t value;

	// spin until not busy
	for (i = 0; i < 1000; i++) {
		value = ntv2_reg_read(ntv2_hin->system_context, ntv2_kona_reg_hdmiin4_edid, ntv2_hin->index);
		value = NTV2_FLD_GET(ntv2_kona_fld_hdmiin4_edid_busy, value);
		if (value == 0) return true;
	}

	return false;
}

static void aux_init(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t aux;

	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxpacketignore0, ntv2_hin->index, 0x07060504);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxpacketignore1, ntv2_hin->index, 0x0b0a0908);
		
	aux = NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxread, 1);
	aux |= NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, 1);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index, aux);

	ntv2EventWaitForSignal(&ntv2_hin->monitor_event, c_aux_time, true);

	aux = NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxread, 1);
	aux |= NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, 0);
	ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index, aux);
}

static int aux_read_ready(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t aux;
	uint32_t active;
	uint32_t read;
	uint32_t write;
	uint32_t bank0;
	uint32_t bank1;
	
	aux = ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index);
	active = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_auxactive, aux);
	read = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_auxread, aux);
	write = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, aux);
	bank0 = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_bank0count, aux);
	bank1 = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_bank1count, aux);

	if (active != write)
		return false;

	if ((active == 0) && (write == 0)) {
		aux = NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxread, 1);
		aux |= NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, write);
		ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index, aux);
		if (bank1 > ntv2_con_auxdata_count)
			bank1 = ntv2_con_auxdata_count;
		return (int)bank1;
	}
	if ((active == 1) && (write == 1)) {
		aux = NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxread, 0);
		aux |= NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, write);
		ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index, aux);
		if (bank0 > ntv2_con_auxdata_count)
			bank0 = ntv2_con_auxdata_count;
		return (int)bank0;
	}

	return 0;
}

static void aux_read_done(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t aux;
	uint32_t active;
	uint32_t read;
	uint32_t write;
	
	aux = ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index);
	active = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_auxactive, aux);
	read = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_auxread, aux);
	write = NTV2_FLD_GET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, aux);

	if ((active == 0) && (write == 0)) {
		aux = NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxread, 1);
		aux |= NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, 1);
		ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index, aux);
	}
	if ((active == 1) && (write == 1)) {
		aux = NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxread, 0);
		aux |= NTV2_FLD_SET(ntv2_fld_hdmiin4_auxcontrol_auxwrite, 0);
		ntv2_reg_write(ntv2_hin->system_context, ntv2_reg_hdmiin4_auxcontrol, ntv2_hin->index, aux);
	}
}

static void aux_read(struct ntv2_hdmiin4 *ntv2_hin, int index, uint32_t *aux_data)
{
	uint32_t regnum = NTV2_REG_NUM(ntv2_reg_hdmiin4_auxdata, (uint32_t)ntv2_hin->index) + (index*ntv2_con_auxdata_size);
	int i;
	
	for (i = 0; i < ntv2_con_auxdata_size; i++) {
		aux_data[i] = ntv2_regnum_read(ntv2_hin->system_context, (regnum + i));
	}
}

static int aux_find(struct ntv2_hdmiin4 *ntv2_hin, int count, uint8_t aux_type)
{
	uint32_t regnum = NTV2_REG_NUM(ntv2_reg_hdmiin4_auxdata, (uint32_t)ntv2_hin->index);
	uint8_t head;
	int i;

	for (i = 0; i < count; i++) {
		head = (uint8_t)ntv2_regnum_read(ntv2_hin->system_context, (regnum + (i*ntv2_con_auxdata_size)));
		if (head == aux_type)
			return i;
	}

	return (-1);
}

static void update_debug_flags(struct ntv2_hdmiin4 *ntv2_hin)
{
	uint32_t val;

	val = ntv2_reg_read(ntv2_hin->system_context, ntv2_reg_hdmi_control, 0);
	val = NTV2_FLD_GET(ntv2_fld_hdmi_debug, val);
	if (val != 0)
	{
		ntv2_user_mask = NTV2_DEBUG_HDMIIN4_STATE | NTV2_DEBUG_HDMIIN4_DETECT | NTV2_DEBUG_ERROR;
	}
	else
	{
		ntv2_user_mask = 0;
	}
}
