ntv2/ajadriver/ntv2hdmiin4.c

1672 lines
70 KiB
C

/*
* SPDX-License-Identifier: MIT
* Copyright (C) 2004 - 2021 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_enable);
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;
}
}