/*
 * DF-4965 / DF-4966 harness — DragonFly dce110 hw_translate
 * =========================================================
 *
 * Verbatim copies of offset_to_id() and id_to_offset() from
 *   sys/dev/drm/amd/display/dc/gpio/dce110/hw_translate_dce110.c
 * with the real register constants from
 *   sys/dev/drm/amd/include/asic_reg/dce/dce_11_0_d.h
 *   sys/dev/drm/amd/include/asic_reg/dce/dce_11_0_sh_mask.h
 *   sys/dev/drm/amd/display/include/gpio_types.h
 *
 * Compile:  gcc -O2 -Wall -Wextra -o harness_dce110 harness_dce110.c
 * Fixed:    gcc -O2 -Wall -Wextra -DFIXED -o harness_fixed harness_dce110.c
 *
 * The dce110 switch constants are ABSOLUTE MMIO offsets (e.g.
 * mmDC_GPIO_DDCVGA_A == 0x4881), and that is exactly the domain the VBIOS
 * GPIO_Pin_LUT stores in usGpioPin_AIndex (bios_parser.c:1819-1820), which
 * get_hpd_gpio() (dc_link.c:115-118) forwards into
 * dal_gpio_service_create_irq() -> offset_to_id().
 */

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

/* ---- enums (gpio_types.h) ---- */
enum gpio_id {
	GPIO_ID_UNKNOWN = (-1),
	GPIO_ID_DDC_DATA,	/* 0 */
	GPIO_ID_DDC_CLOCK,	/* 1 */
	GPIO_ID_GENERIC,	/* 2 */
	GPIO_ID_HPD,		/* 3 */
	GPIO_ID_GPIO_PAD,	/* 4 */
	GPIO_ID_VIP_PAD,	/* 5 */
	GPIO_ID_SYNC,		/* 6 */
	GPIO_ID_GSL,		/* 7 */
	GPIO_ID_COUNT,
};
enum gpio_generic { GPIO_GENERIC_A, GPIO_GENERIC_B, GPIO_GENERIC_C,
	GPIO_GENERIC_D, GPIO_GENERIC_E, GPIO_GENERIC_F, GPIO_GENERIC_G };
enum gpio_hpd { GPIO_HPD_1, GPIO_HPD_2, GPIO_HPD_3,
	GPIO_HPD_4, GPIO_HPD_5, GPIO_HPD_6 };
enum gpio_sync { GPIO_SYNC_HSYNC_A, GPIO_SYNC_VSYNC_A,
	GPIO_SYNC_HSYNC_B, GPIO_SYNC_VSYNC_B };
enum gpio_gsl { GPIO_GSL_GENLOCK_CLOCK, GPIO_GSL_GENLOCK_VSYNC,
	GPIO_GSL_SWAPLOCK_A, GPIO_GSL_SWAPLOCK_B };
enum gpio_ddc_line { GPIO_DDC_LINE_DDC1, GPIO_DDC_LINE_DDC2,
	GPIO_DDC_LINE_DDC3, GPIO_DDC_LINE_DDC4, GPIO_DDC_LINE_DDC5,
	GPIO_DDC_LINE_DDC6, GPIO_DDC_LINE_DDC_VGA, GPIO_DDC_LINE_I2C_PAD };
#define GPIO_GPIO_PAD_MAX 30

/* ---- mm offsets (dce_11_0_d.h) ---- */
#define mmDC_GPIO_DEBUG			0x4804
#define mmGPIOPAD_A			0x183	/* smu_7_1_0_d.h (dce80-family) */
#define mmDC_GPIO_GENERIC_A		0x4861
#define mmDC_GPIO_DDC1_A		0x4869
#define mmDC_GPIO_DDC2_A		0x486d
#define mmDC_GPIO_DDC3_A		0x4871
#define mmDC_GPIO_DDC4_A		0x4875
#define mmDC_GPIO_DDC5_A		0x4879
#define mmDC_GPIO_DDC6_A		0x487d
#define mmDC_GPIO_DDCVGA_A		0x4881
#define mmDC_GPIO_SYNCA_A		0x4885
#define mmDC_GPIO_GENLK_A		0x4889
#define mmDC_GPIO_HPD_A			0x488d
#define mmDC_GPIO_PWRSEQ_A		0x4891
#define mmDC_GPIO_PAD_STRENGTH_1	0x4894
#define mmDC_GPIO_PAD_STRENGTH_2	0x4895
#define mmDC_GPIO_I2CPAD_A		0x4899

/* ---- field masks (dce_11_0_sh_mask.h) ---- */
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK	0x1
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK	0x100
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK	0x10000
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK	0x100000
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK	0x200000
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK	0x400000
#define DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK	0x800000
#define DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK		0x1
#define DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK		0x100
#define DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK		0x10000
#define DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK		0x1000000
#define DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK		0x4000000
#define DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK		0x10000000
#define DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK		0x1
#define DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK		0x100
#define DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK	0x1
#define DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK	0x100
#define DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK	0x10000
#define DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK	0x1000000
#define DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK		0x100
#define DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK		0x1
/* the register's REAL field layout (used by the SDA check): */
#define DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK		0x1
#define DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK		0x2
#define DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK		0x100

#define ASSERT_CRITICAL(x)	do { } while (0)

/* ============ VERBATIM from hw_translate_dce110.c:39-179 ============ */
static bool offset_to_id(
	uint32_t offset,
	uint32_t mask,
	enum gpio_id *id,
	uint32_t *en)
{
	switch (offset) {
	/* GENERIC */
	case mmDC_GPIO_GENERIC_A:
		*id = GPIO_ID_GENERIC;
		switch (mask) {
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
			*en = GPIO_GENERIC_A;
			return true;
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
			*en = GPIO_GENERIC_B;
			return true;
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
			*en = GPIO_GENERIC_C;
			return true;
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
			*en = GPIO_GENERIC_D;
			return true;
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
			*en = GPIO_GENERIC_E;
			return true;
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
			*en = GPIO_GENERIC_F;
			return true;
		case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK:
			*en = GPIO_GENERIC_G;
			return true;
		default:
			ASSERT_CRITICAL(false);
			return false;
		}
	break;
	/* HPD */
	case mmDC_GPIO_HPD_A:
		*id = GPIO_ID_HPD;
		switch (mask) {
		case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
			*en = GPIO_HPD_1;
			return true;
		case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
			*en = GPIO_HPD_2;
			return true;
		case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
			*en = GPIO_HPD_3;
			return true;
		case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
			*en = GPIO_HPD_4;
			return true;
		case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
			*en = GPIO_HPD_5;
			return true;
		case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK:
			*en = GPIO_HPD_6;
			return true;
		default:
			ASSERT_CRITICAL(false);
			return false;
		}
	break;
	/* SYNCA */
	case mmDC_GPIO_SYNCA_A:
		*id = GPIO_ID_SYNC;
		switch (mask) {
		case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK:
			*en = GPIO_SYNC_HSYNC_A;
			return true;
		case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK:
			*en = GPIO_SYNC_VSYNC_A;
			return true;
		default:
			ASSERT_CRITICAL(false);
			return false;
		}
	break;
	/* mmDC_GPIO_GENLK_MASK */
	case mmDC_GPIO_GENLK_A:
		*id = GPIO_ID_GSL;
		switch (mask) {
		case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
			*en = GPIO_GSL_GENLOCK_CLOCK;
			return true;
		case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
			*en = GPIO_GSL_GENLOCK_VSYNC;
			return true;
		case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
			*en = GPIO_GSL_SWAPLOCK_A;
			return true;
		case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
			*en = GPIO_GSL_SWAPLOCK_B;
			return true;
		default:
			ASSERT_CRITICAL(false);
			return false;
		}
	break;
	/* DDC */
	/* we don't care about the GPIO_ID for DDC
	 * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
	 * directly in the create method */
	case mmDC_GPIO_DDC1_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC1;
		return true;
	case mmDC_GPIO_DDC2_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC2;
		return true;
	case mmDC_GPIO_DDC3_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC3;
		return true;
	case mmDC_GPIO_DDC4_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC4;
		return true;
	case mmDC_GPIO_DDC5_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC5;
		return true;
	case mmDC_GPIO_DDC6_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC6;
		return true;
	case mmDC_GPIO_DDCVGA_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_DDC_VGA;
		return true;
	/* GPIO_I2CPAD */
	case mmDC_GPIO_I2CPAD_A:
#ifdef FIXED
		*id = GPIO_ID_DDC_DATA;
#endif
		*en = GPIO_DDC_LINE_I2C_PAD;
		return true;
	/* Not implemented */
	case mmDC_GPIO_PWRSEQ_A:
	case mmDC_GPIO_PAD_STRENGTH_1:
	case mmDC_GPIO_PAD_STRENGTH_2:
	case mmDC_GPIO_DEBUG:
		return false;
	/* UNEXPECTED */
	default:
		ASSERT_CRITICAL(false);
		return false;
	}
}

/* ============ VERBATIM from hw_translate_dce110.c:181-366 ============ */
struct gpio_pin_info {
	uint32_t offset;
	uint32_t offset_y;
	uint32_t offset_en;
	uint32_t offset_mask;
	uint32_t mask;
	uint32_t mask_y;
	uint32_t mask_en;
	uint32_t mask_mask;
};

static bool id_to_offset(
	enum gpio_id id,
	uint32_t en,
	struct gpio_pin_info *info)
{
	bool result = true;

	switch (id) {
	case GPIO_ID_DDC_DATA:
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
		switch (en) {
		case GPIO_DDC_LINE_DDC1:
			info->offset = mmDC_GPIO_DDC1_A;
		break;
		case GPIO_DDC_LINE_DDC2:
			info->offset = mmDC_GPIO_DDC2_A;
		break;
		case GPIO_DDC_LINE_DDC3:
			info->offset = mmDC_GPIO_DDC3_A;
		break;
		case GPIO_DDC_LINE_DDC4:
			info->offset = mmDC_GPIO_DDC4_A;
		break;
		case GPIO_DDC_LINE_DDC5:
			info->offset = mmDC_GPIO_DDC5_A;
		break;
		case GPIO_DDC_LINE_DDC6:
			info->offset = mmDC_GPIO_DDC6_A;
		break;
		case GPIO_DDC_LINE_DDC_VGA:
			info->offset = mmDC_GPIO_DDCVGA_A;
		break;
		case GPIO_DDC_LINE_I2C_PAD:
			info->offset = mmDC_GPIO_I2CPAD_A;
		break;
		default:
			ASSERT_CRITICAL(false);
			result = false;
		}
	break;
	case GPIO_ID_DDC_CLOCK:
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK;
		switch (en) {
		case GPIO_DDC_LINE_DDC1:
			info->offset = mmDC_GPIO_DDC1_A;
		break;
		case GPIO_DDC_LINE_DDC2:
			info->offset = mmDC_GPIO_DDC2_A;
		break;
		case GPIO_DDC_LINE_DDC3:
			info->offset = mmDC_GPIO_DDC3_A;
		break;
		case GPIO_DDC_LINE_DDC4:
			info->offset = mmDC_GPIO_DDC4_A;
		break;
		case GPIO_DDC_LINE_DDC5:
			info->offset = mmDC_GPIO_DDC5_A;
		break;
		case GPIO_DDC_LINE_DDC6:
			info->offset = mmDC_GPIO_DDC6_A;
		break;
		case GPIO_DDC_LINE_DDC_VGA:
			info->offset = mmDC_GPIO_DDCVGA_A;
		break;
		case GPIO_DDC_LINE_I2C_PAD:
			info->offset = mmDC_GPIO_I2CPAD_A;
		break;
		default:
			ASSERT_CRITICAL(false);
			result = false;
		}
	break;
	case GPIO_ID_GENERIC:
		info->offset = mmDC_GPIO_GENERIC_A;
		switch (en) {
		case GPIO_GENERIC_A:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
		break;
		case GPIO_GENERIC_B:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
		break;
		case GPIO_GENERIC_C:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
		break;
		case GPIO_GENERIC_D:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
		break;
		case GPIO_GENERIC_E:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
		break;
		case GPIO_GENERIC_F:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
		break;
		case GPIO_GENERIC_G:
			info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK;
		break;
		default:
			ASSERT_CRITICAL(false);
			result = false;
		}
	break;
	case GPIO_ID_HPD:
		info->offset = mmDC_GPIO_HPD_A;
		switch (en) {
		case GPIO_HPD_1:
			info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
		break;
		case GPIO_HPD_2:
			info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
		break;
		case GPIO_HPD_3:
			info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
		break;
		case GPIO_HPD_4:
			info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
		break;
		case GPIO_HPD_5:
			info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
		break;
		case GPIO_HPD_6:
			info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK;
		break;
		default:
			ASSERT_CRITICAL(false);
			result = false;
		}
	break;
	case GPIO_ID_SYNC:
		switch (en) {
		case GPIO_SYNC_HSYNC_A:
			info->offset = mmDC_GPIO_SYNCA_A;
			info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK;
		break;
		case GPIO_SYNC_VSYNC_A:
			info->offset = mmDC_GPIO_SYNCA_A;
			info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK;
		break;
		case GPIO_SYNC_HSYNC_B:
		case GPIO_SYNC_VSYNC_B:
		default:
			ASSERT_CRITICAL(false);
			result = false;
		}
	break;
	case GPIO_ID_GSL:
		switch (en) {
		case GPIO_GSL_GENLOCK_CLOCK:
			info->offset = mmDC_GPIO_GENLK_A;
			info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK;
		break;
		case GPIO_GSL_GENLOCK_VSYNC:
			info->offset = mmDC_GPIO_GENLK_A;
			info->mask =
				DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK;
		break;
		case GPIO_GSL_SWAPLOCK_A:
			info->offset = mmDC_GPIO_GENLK_A;
			info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK;
		break;
		case GPIO_GSL_SWAPLOCK_B:
			info->offset = mmDC_GPIO_GENLK_A;
			info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK;
		break;
		default:
			ASSERT_CRITICAL(false);
			result = false;
		}
	break;
	case GPIO_ID_VIP_PAD:
	default:
		ASSERT_CRITICAL(false);
		result = false;
	}

	if (result) {
		info->offset_y = info->offset + 2;
		info->offset_en = info->offset + 1;
		info->offset_mask = info->offset - 1;

		info->mask_y = info->mask;
		info->mask_en = info->mask;
		info->mask_mask = info->mask;
	}

	return result;
}

/* ==================================================================== */

#define POISON 0xA11CE0D5u

static const char *idname(int i)
{
	switch (i) {
	case GPIO_ID_DDC_DATA:	return "DDC_DATA(0)";
	case GPIO_ID_DDC_CLOCK:	return "DDC_CLOCK(1)";
	case GPIO_ID_GENERIC:	return "GENERIC(2)";
	case GPIO_ID_HPD:	return "HPD(3)";
	case GPIO_ID_GPIO_PAD:	return "GPIO_PAD(4)";
	case GPIO_ID_VIP_PAD:	return "VIP_PAD(5)";
	case GPIO_ID_SYNC:	return "SYNC(6)";
	case GPIO_ID_GSL:	return "GSL(7)";
	default:		return "?";
	}
}

static void t_offset(const char *tag, uint32_t off, uint32_t mask)
{
	enum gpio_id id = (enum gpio_id)POISON;
	uint32_t en = POISON;
	bool r = offset_to_id(off, mask, &id, &en);

	printf("T[%s] offset_to_id(0x%04x, 0x%x) = %s en=%u id=%d(%s) %s\n",
	       tag, off, mask, r ? "true" : "false", en, (int)id, idname(id),
	       ((uint32_t)id == POISON) ? "<== id UNWRITTEN" : "");
}

int main(void)
{
	printf("== DF-4965: dce110 offset_to_id DDC-family branches ==\n");
	/* The 8 DDC-family offsets with VBIOS-plausible masks
	 * (bios_parser.c:1825 builds mask = 1 << ucGpioPinBitShift) */
	t_offset("DDC1    ", mmDC_GPIO_DDC1_A,   DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK);
	t_offset("DDC2    ", mmDC_GPIO_DDC2_A,   0x100);
	t_offset("DDC3    ", mmDC_GPIO_DDC3_A,   0x100);
	t_offset("DDC4    ", mmDC_GPIO_DDC4_A,   0x100);
	t_offset("DDC5    ", mmDC_GPIO_DDC5_A,   0x100);
	t_offset("DDC6    ", mmDC_GPIO_DDC6_A,   DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK);
	t_offset("DDCVGA  ", mmDC_GPIO_DDCVGA_A, 0x100);
	t_offset("I2CPAD  ", mmDC_GPIO_I2CPAD_A, 0x1);

	printf("\n== controls (id IS written / rejected) ==\n");
	t_offset("GENERIC ", mmDC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK);
	t_offset("HPD     ", mmDC_GPIO_HPD_A,     DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK);
	t_offset("SYNCA   ", mmDC_GPIO_SYNCA_A,   DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK);
	t_offset("PWRSEQ  ", mmDC_GPIO_PWRSEQ_A,  0x1);
	t_offset("DEBUG   ", mmDC_GPIO_DEBUG,     0x1);
	t_offset("PADSTR1 ", mmDC_GPIO_PAD_STRENGTH_1, 0x1);
	t_offset("BOGUS   ", 0x4fff, 0x1);
	/* DF-4942 negative: the dce80 GPIOPAD producer must not exist here */
	t_offset("GPIOPAD ", mmGPIOPAD_A, 0x1);

	printf("\n== consumer chain (dal_gpio_service_create_irq, gpio_service.c:148) ==\n");
	printf("   id uninitialized; if garbage == GPIO_ID_HPD(3):\n");
	printf("   dal_gpio_create_irq(HPD, en) -> dal_gpio_service_open(HPD, en)\n");
	printf("   -> define_hpd_registers(pin, en) -> &hpd_regs[en]\n");
	printf("   hpd_regs[] has 6 entries (hw_factory_dce110.c:57-64); en=6\n");
	printf("   (DDC_VGA) and en=7 (I2C_PAD) index 1 and 2 entries OOB ->\n");
	printf("   garbage int_status/toggle_filt_cntl descriptors drive MMIO\n");
	printf("   (hw_hpd.c REG_GET/REG_UPDATE).\n");

	printf("\n== DF-4966: I2C pad SDA wrong-bit ==\n");
	{
		struct gpio_pin_info info;
		bool r;

		r = id_to_offset(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_I2C_PAD, &info);
		printf("id_to_offset(DDC_DATA, I2C_PAD)   = %s offset=0x%04x mask=0x%x\n",
		       r ? "true" : "false", info.offset, info.mask);
		printf("  real DC_GPIO_I2CPAD_A fields: SCL=0x%x SDA=0x%x  -> mask 0x%x %s\n",
		       DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK,
		       DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK,
		       info.mask,
		       info.mask == DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK ?
		       "CORRECT (==SDA)" :
		       "WRONG: bit 8 does not exist in DC_GPIO_I2CPAD_A (SDA is bit 1)");

		r = id_to_offset(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_I2C_PAD, &info);
		printf("id_to_offset(DDC_CLOCK, I2C_PAD)  = %s offset=0x%04x mask=0x%x %s\n",
		       r ? "true" : "false", info.offset, info.mask,
		       info.mask == DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK ?
		       "(==SCL, correct by coincidence)" : "(WRONG)");

		r = id_to_offset(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, &info);
		printf("id_to_offset(DDC_DATA, DDC1)      = %s offset=0x%04x mask=0x%x %s\n",
		       r ? "true" : "false", info.offset, info.mask,
		       info.mask == DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK ?
		       "(==DDC1DATA, shared mask correct for real DDC lines)" :
		       "(WRONG)");

		r = id_to_offset(GPIO_ID_GPIO_PAD, 5, &info);
		printf("id_to_offset(GPIO_PAD, 5)         = %s (no GPIO_PAD branch: no DF-4944 twin)\n",
		       r ? "true" : "false");
	}
	return 0;
}
