From e6f372d6f357184bedb18b4384e3c752bb60d342 Mon Sep 17 00:00:00 2001
From: Dalon Westergreen <dwesterg@gmail.com>
Date: Sun, 12 Feb 2017 14:22:46 -0800
Subject: [PATCH 5/6] Add DE10-Nano HDMI configuration and debug apps

Signed-off-by: Dalon Westergreen <dwesterg@gmail.com>
---
 examples/standalone/Makefile                |    3 +
 examples/standalone/de10_nano_hdmi_config.c | 1324 +++++++++++++++++++++++++++
 examples/standalone/de10_nano_hdmi_config.h |  195 ++++
 examples/standalone/dump_adv7513_edid.c     |  697 ++++++++++++++
 examples/standalone/dump_adv7513_regs.c     |  129 +++
 5 files changed, 2348 insertions(+)
 create mode 100644 examples/standalone/de10_nano_hdmi_config.c
 create mode 100644 examples/standalone/de10_nano_hdmi_config.h
 create mode 100644 examples/standalone/dump_adv7513_edid.c
 create mode 100644 examples/standalone/dump_adv7513_regs.c

diff --git a/examples/standalone/Makefile b/examples/standalone/Makefile
index 5a6ae00..c23ac50 100644
--- a/examples/standalone/Makefile
+++ b/examples/standalone/Makefile
@@ -6,6 +6,9 @@
 #
 
 extra-y        := hello_world
+extra-y        += de10_nano_hdmi_config
+extra-y        += dump_adv7513_regs
+extra-y        += dump_adv7513_edid
 extra-$(CONFIG_SMC91111)           += smc91111_eeprom
 extra-$(CONFIG_SMC911X)            += smc911x_eeprom
 extra-$(CONFIG_SPI_FLASH_ATMEL)    += atmel_df_pow2
diff --git a/examples/standalone/de10_nano_hdmi_config.c b/examples/standalone/de10_nano_hdmi_config.c
new file mode 100644
index 0000000..a7dd2c1
--- /dev/null
+++ b/examples/standalone/de10_nano_hdmi_config.c
@@ -0,0 +1,1324 @@
+/*
+ * The MIT License (MIT)
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <common.h>
+#include <exports.h>
+#include "de10_nano_hdmi_config.h"
+
+/*
+This program is built to run as a u-boot standalone application and leverage the
+u-boot provided runtime environment.  Prior to running this program you will
+need to configure a few things in the u-boot environment.
+
+run fpga_cfg
+i2c dev 2
+load mmc 0:1 0x0c300000 STARTUP.BMP
+dcache flush
+dcache off
+
+load this program into memory at 0x0C100000 and then run it at 0x0C100001, yes
+you set the lsb of the address to indicate a thumb mode branch or something like
+that.
+
+load mmc 0:1 0x0c100000 de10_nano_hdmi_config.bin
+go 0x0C100001
+
+*/
+
+/* ADV7513 register configurations */
+init_config init_config_array[] = {
+	{0x98, 0x03},	// must be set
+	{0x9A, 0xE0},	// must be set
+	{0x9C, 0x30},	// must be set
+	{0x9D, 0x61},	// must be set
+	{0xA2, 0xA4},	// must be set
+	{0xA3, 0xA4},	// must be set
+	{0xE0, 0xD0},	// must be set
+	{0xF9, 0x00},	// must be set
+	{0x16, 0x30},	// 8-bit color depth
+	{0x17, 0x02},	// aspect ratio 16:9, modified below if needed
+	{0xAF, 0x06},	// HDMI mode, no HDCP
+	{0x0C, 0x00},	// disable I2S inputs
+	{0x96, 0xF6},	// clear all interrupts
+};
+
+/* prototypes */
+void pll_calc_fixed(struct pll_calc_struct *the_pll_calc_struct);
+void uitoa(uint32_t uint32_input, char **output_str);
+
+/* main configuration function */
+int de10_nano_hdmi_config(int argc, char * const argv[]) {
+
+	int i;
+	int j;
+	int result;
+	char *print_str;
+	uint8_t adv7513_read_buffer[256];
+	uint8_t adv7513_edid_buffer[256];
+	uint8_t adv7513_write_val;
+	uint8_t checksum;
+	uint8_t edid_header_pattern_array[8] = {
+			0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
+	uint8_t *descriptor_base;
+	uint8_t dtd_offset;
+	uint8_t dtd_count;
+
+	uint16_t pixel_clock;
+	uint16_t horizontal_active_pixels;
+	uint16_t horizontal_blanking_pixels;
+	uint16_t vertical_active_lines;
+	uint16_t vertical_blanking_lines;
+	uint16_t horizontal_sync_offset;
+	uint16_t horizontal_sync_width;
+	uint8_t vertical_sync_offset;
+	uint8_t vertical_sync_width;
+	uint8_t interlaced;
+
+	uint32_t pixel_clock_MHz;
+	uint8_t bad_pixel_clock_MHz;
+	uint8_t bad_horizontal_active_pixels_value;
+	uint8_t bad_vertical_active_lines_value;
+	uint8_t bad_interlaced_value;
+	uint8_t valid_timing_configuration_located;
+	uint8_t monitor_connected;
+
+	int32_t aspect_ratio;
+
+	uint32_t N_reg;
+	uint32_t M_reg;
+	uint32_t C_reg;
+	uint32_t K_reg;
+	uint32_t BW_reg;
+	uint32_t CP_reg;
+	uint32_t VCODIV_reg;
+
+	volatile uint32_t *pll_ptr;
+	volatile uint32_t *fbr_ptr;
+	volatile uint32_t *cvo_ptr;
+	volatile uint32_t *cvo_reset_pio_ptr;
+	volatile uint32_t *pll_reset_pio_ptr;
+	volatile uint32_t *pll_locked_pio_ptr;
+	volatile uint32_t *video_ptr;
+	volatile struct bmp_image_header *bmp_header_ptr;
+	volatile struct bmp_24_bit_pixel *bmp_pixel_ptr;
+
+	struct pll_calc_struct shared_struct;
+
+	char snprintf_buffer[256];
+	char *snprintf_buffer_ptr;
+	uint32_t milestones;
+
+	/* initialize u-boot application environment */
+	app_startup(argv);
+
+	/* initialize pointers and status */
+	setenv(HDMI_STATUS_ENV, "startup");
+	setenv(HDMI_INFO_ENV, "none");
+	setenv(HDMI_ERROR_ENV, "none");
+
+        pll_ptr = (uint32_t*)(LWH2F_BASE + PLL_RECNFG_BASE);
+	fbr_ptr = (uint32_t*)(LWH2F_BASE + FBR_BASE);
+	cvo_ptr = (uint32_t*)(LWH2F_BASE + CVO_BASE);
+	cvo_reset_pio_ptr = (uint32_t*)(LWH2F_BASE + CVO_RESET_PIO_BASE);
+	pll_reset_pio_ptr = (uint32_t*)(LWH2F_BASE + PLL_RESET_PIO_BASE);
+	pll_locked_pio_ptr = (uint32_t*)(LWH2F_BASE + PLL_LOCKED_PIO_BASE);
+	video_ptr = (uint32_t*)VIDEO_BUFFER;
+
+	valid_timing_configuration_located = 0;
+	milestones = 0;
+
+	/* since we cannot read a single register from the ADV7513 with the
+	 * default I2C driver, we just read the first N registers starting
+	 * from ZERO and reaching up to the register we want
+	 */
+	setenv(HDMI_STATUS_ENV, "read ADV7513 chip ID");
+	milestones |= 0x01 << 0;
+	result = i2c_read(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			0x00,			// unsigned int addr
+			0,			// int alen
+			adv7513_read_buffer,	// uint8_t *buffer
+			ADV7513_CHIP_ID_LO + 1	// int len
+		);
+
+	if(result != 0) {
+		print_str = "reading I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(1);
+	}
+
+	/* verify the chip id for the ADV7513 */
+	setenv(HDMI_STATUS_ENV, "verify ADV7513 chip ID");
+	milestones |= 0x01 << 1;
+	if(
+		(adv7513_read_buffer[ADV7513_CHIP_ID_HI] !=
+			ADV7513_CHIP_ID_HI_VAL) ||
+		(adv7513_read_buffer[ADV7513_CHIP_ID_LO] !=
+			ADV7513_CHIP_ID_LO_VAL) ) {
+
+		print_str = "Bad Chip ID";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(2);
+	}
+
+	/* verify that we see a monitor attached to the HDMI connector */
+	setenv(HDMI_STATUS_ENV, "verify monitor attached");
+	milestones |= 0x01 << 2;
+	monitor_connected = 1;
+	if((adv7513_read_buffer[ADV7513_HPD_MNSNS] & ADV7513_HPD_MNSNS_BITS) !=
+		ADV7513_HPD_MNSNS_BITS) {
+
+		print_str = "No HDMI display detected";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		monitor_connected = 0;
+	}
+
+	/* force HPD true */
+	setenv(HDMI_STATUS_ENV, "force HPD true");
+	milestones |= 0x01 << 3;
+	adv7513_write_val = ADV7513_HPD_CNTL_BITS;
+	result = i2c_write(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			ADV7513_HPD_CNTL,	// unsigned int addr
+			1,			// int alen
+			&adv7513_write_val,	// uint8_t *buffer
+			1			// int len
+		);
+
+	if(result != 0) {
+		print_str = "writing I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(4);
+	}
+
+	/* power down the ADV7513 */
+	setenv(HDMI_STATUS_ENV, "power down ADV7513");
+	milestones |= 0x01 << 4;
+	adv7513_write_val = adv7513_read_buffer[ADV7513_PWR_DWN];
+	adv7513_write_val |= ADV7513_PWR_DWN_BIT;
+	result = i2c_write(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			ADV7513_PWR_DWN,	// unsigned int addr
+			1,			// int alen
+			&adv7513_write_val,	// uint8_t *buffer
+			1			// int len
+		);
+
+	if(result != 0) {
+		print_str = "writing I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(4);
+	}
+
+	/* power up the ADV7513 */
+	setenv(HDMI_STATUS_ENV, "power up ADV7513");
+	milestones |= 0x01 << 5;
+	adv7513_write_val &= ~ADV7513_PWR_DWN_BIT;
+	result = i2c_write(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			ADV7513_PWR_DWN,	// unsigned int addr
+			1,			// int alen
+			&adv7513_write_val,	// uint8_t *buffer
+			1			// int len
+		);
+
+	if(result != 0) {
+		print_str = "writing I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(5);
+	}
+
+	/*
+	if we sense a monitor is attached then we attempt to read the EDID data
+	and configure ourselves according to that.  Otherwise we apply a default
+	1024*728 configuration
+	*/
+	setenv(HDMI_STATUS_ENV, "monitor present fork");
+	milestones |= 0x01 << 6;
+	if(monitor_connected == 0)
+		goto post_EDID_evaluation;
+
+	/* wait for the EDID data to become ready */
+	setenv(HDMI_STATUS_ENV, "wait EDID ready");
+	milestones |= 0x01 << 7;
+	for(i = 0 ; i < 1000 ; i++) {
+		result = i2c_read(
+				ADV7513_MAIN_ADDR,	// uint8_t chip
+				0x00,			// unsigned int addr
+				0,			// int alen
+				adv7513_read_buffer,	// uint8_t *buffer
+				ADV7513_EDID_RDY + 1	// int len
+			);
+
+		if(result != 0) {
+			print_str = "reading I2C";
+			printf("%s%s\n", ERROR_STR, print_str);
+			setenv(HDMI_ERROR_ENV, print_str);
+			print_str = "HDMI_milestones";
+			snprintf_buffer_ptr = &snprintf_buffer[0];
+			uitoa(milestones, &snprintf_buffer_ptr);
+			setenv(print_str, snprintf_buffer);
+			printf("%s = %s\n", print_str, snprintf_buffer);
+			return(6);
+		}
+
+		if((adv7513_read_buffer[ADV7513_EDID_RDY] &
+			ADV7513_EDID_RDY_BIT) == ADV7513_EDID_RDY_BIT)
+			break;
+	}
+
+	if(i >= 1000) {
+		print_str = "EDID timeout";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		goto post_EDID_evaluation;
+	}
+
+	/* read the EDID data */
+	setenv(HDMI_STATUS_ENV, "read EDID data");
+	milestones |= 0x01 << 8;
+	result = i2c_read(
+			ADV7513_EDID_ADDR,	// uint8_t chip
+			0x00,			// unsigned int addr
+			0,			// int alen
+			adv7513_edid_buffer,	// uint8_t *buffer
+			256			// int len
+		);
+
+	if(result != 0) {
+		print_str = "reading I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(8);
+	}
+
+	/* verify EDID block 0 checksum */
+	setenv(HDMI_STATUS_ENV, "verify EDID block 0 checksum");
+	milestones |= 0x01 << 9;
+	checksum = 0;
+	for(i = 0 ; i < 128 ; i++)
+		checksum += adv7513_edid_buffer[i];
+
+	if(checksum != 0) {
+		print_str = "EDID block 0 checksum";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		goto post_EDID_evaluation;
+	}
+
+	/* verify block 0 header pattern */
+	setenv(HDMI_STATUS_ENV, "verify block 0 header pattern");
+	milestones |= 0x01 << 10;
+	for(i = 0 ; i < 8 ; i++) {
+		if(edid_header_pattern_array[i] != adv7513_edid_buffer[i]) {
+			print_str = "EDID header pattern";
+			printf("%s%s\n", WARN_STR, print_str);
+			setenv(HDMI_ERROR_ENV, print_str);
+			goto post_EDID_evaluation;
+		}
+	}
+
+	/* decode descriptor blocks */
+	setenv(HDMI_STATUS_ENV, "decode EDID block 0");
+	milestones |= 0x01 << 11;
+	bad_horizontal_active_pixels_value = 0;
+	bad_vertical_active_lines_value = 0;
+	bad_interlaced_value = 0;
+	for(i = 0 ; i < 4 ; i++) {
+		descriptor_base = &adv7513_edid_buffer[54];
+		descriptor_base += i * 18;
+		if(
+			(descriptor_base[0] == 0) &&
+			(descriptor_base[1] == 0) &&
+			(descriptor_base[2] == 0) &&
+			(descriptor_base[4] == 0)
+		  ) {
+			/* not a Detailed Timing Descriptor */
+			continue;
+		}
+		/* Detailed Timing Descriptor
+		 * extract the relevant fields of the descriptor
+		 */
+		pixel_clock =
+			(descriptor_base[1] << 8) |
+			descriptor_base[0];
+
+		horizontal_active_pixels =
+			(((descriptor_base[4] >> 4) & 0x0F) << 8) |
+			descriptor_base[2];
+
+		horizontal_blanking_pixels =
+			((descriptor_base[4] & 0x0F) << 8) |
+			descriptor_base[3];
+
+		vertical_active_lines =
+			(((descriptor_base[7] >> 4) & 0x0F) << 8) |
+			descriptor_base[5];
+
+		vertical_blanking_lines =
+			((descriptor_base[7] & 0x0F) << 8) |
+			descriptor_base[6];
+
+		horizontal_sync_offset =
+			(((descriptor_base[11] >> 6) & 0x03) << 8) |
+			descriptor_base[8];
+
+		horizontal_sync_width =
+			(((descriptor_base[11] >> 4) & 0x03) << 8) |
+			descriptor_base[9];
+
+		vertical_sync_offset =
+			(((descriptor_base[11] >> 2) & 0x03) << 4) |
+			((descriptor_base[10] >> 4) & 0x0F);
+
+		vertical_sync_width =
+			((descriptor_base[11] & 0x03) << 4) |
+			(descriptor_base[10] & 0x0F);
+
+		interlaced = (descriptor_base[17] & 0x80) ? 1 : 0;
+
+		/* adjust pixel clock up to MHz */
+		pixel_clock_MHz = pixel_clock * 10000;
+
+		/* check for valid ranges of key parameters */
+		if((pixel_clock_MHz > 150000000) ||
+			(pixel_clock_MHz < 60000000)) {
+			bad_pixel_clock_MHz++;
+			continue;
+		}
+		if((horizontal_active_pixels > 1920) ||
+			(horizontal_active_pixels < 1280)) {
+			bad_horizontal_active_pixels_value++;
+			continue;
+		}
+		if((vertical_active_lines > 1080) ||
+			(vertical_active_lines < 720)) {
+			bad_vertical_active_lines_value++;
+			continue;
+		}
+		if(interlaced != 0) {
+			bad_interlaced_value++;
+			continue;
+		}
+		valid_timing_configuration_located = 1;
+		break;
+	}
+
+	if(valid_timing_configuration_located != 0)
+		goto post_EDID_evaluation;
+
+	/* check for extension blocks */
+	setenv(HDMI_STATUS_ENV, "check for extension blocks");
+	milestones |= 0x01 << 31;
+	if(adv7513_edid_buffer[126] == 0)
+		goto post_EDID_evaluation;
+
+	/* verify extension block checksum */
+	setenv(HDMI_STATUS_ENV, "verify extension block checksum");
+	milestones |= 0x01 << 30;
+	checksum = 0;
+	for(i = 0 ; i < 128 ; i++)
+		checksum += adv7513_edid_buffer[128 + i];
+
+	if(checksum != 0) {
+		print_str = "extension block 1 checksum";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		goto post_EDID_evaluation;
+	}
+
+	/* verify extension tag */
+	setenv(HDMI_STATUS_ENV, "verify extension tag");
+	milestones |= 0x01 << 29;
+	if(adv7513_edid_buffer[128] != 0x02) {
+		print_str = "extension tag";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		goto post_EDID_evaluation;
+	}
+
+	/* verify revision number */
+	setenv(HDMI_STATUS_ENV, "verify revision number");
+	milestones |= 0x01 << 28;
+	if(adv7513_edid_buffer[129] != 0x03) {
+		print_str = "extension revision number";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		goto post_EDID_evaluation;
+	}
+
+	/* check for DTDs in extension block */
+	setenv(HDMI_STATUS_ENV, "check for DTDs in extension block");
+	milestones |= 0x01 << 27;
+	dtd_offset = adv7513_edid_buffer[130];
+	dtd_count = adv7513_edid_buffer[131] & 0x0F;
+	if(
+		(dtd_offset == 0x00) ||
+		(dtd_count == 0x00)
+	  ) {
+		print_str = "No DTDs present in extension block";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		goto post_EDID_evaluation;
+	}
+
+	/* decode descriptor blocks */
+	setenv(HDMI_STATUS_ENV, "decode EDID block 1");
+	milestones |= 0x01 << 26;
+	for(i = 0 ; i < dtd_count ; i++) {
+		descriptor_base = &adv7513_edid_buffer[128];
+		descriptor_base += dtd_offset;
+		descriptor_base += i * 18;
+		if(
+			(descriptor_base[0] == 0) &&
+			(descriptor_base[1] == 0) &&
+			(descriptor_base[2] == 0) &&
+			(descriptor_base[4] == 0)
+		  ) {
+			/* not a Detailed Timing Descriptor */
+			continue;
+		}
+		/* Detailed Timing Descriptor
+		 * extract the relevant fields of the descriptor
+		 */
+		pixel_clock =
+			(descriptor_base[1] << 8) |
+			descriptor_base[0];
+
+		horizontal_active_pixels =
+			(((descriptor_base[4] >> 4) & 0x0F) << 8) |
+			descriptor_base[2];
+
+		horizontal_blanking_pixels =
+			((descriptor_base[4] & 0x0F) << 8) |
+			descriptor_base[3];
+
+		vertical_active_lines =
+			(((descriptor_base[7] >> 4) & 0x0F) << 8) |
+			descriptor_base[5];
+
+		vertical_blanking_lines =
+			((descriptor_base[7] & 0x0F) << 8) |
+			descriptor_base[6];
+
+		horizontal_sync_offset =
+			(((descriptor_base[11] >> 6) & 0x03) << 8) |
+			descriptor_base[8];
+
+		horizontal_sync_width =
+			(((descriptor_base[11] >> 4) & 0x03) << 8) |
+			descriptor_base[9];
+
+		vertical_sync_offset =
+			(((descriptor_base[11] >> 2) & 0x03) << 4) |
+			((descriptor_base[10] >> 4) & 0x0F);
+
+		vertical_sync_width =
+			((descriptor_base[11] & 0x03) << 4) |
+			(descriptor_base[10] & 0x0F);
+
+		interlaced = (descriptor_base[17] & 0x80) ? 1 : 0;
+
+		/* adjust pixel clock up to MHz */
+		pixel_clock_MHz = pixel_clock * 10000;
+
+		/* check for valid ranges of key parameters */
+		if((pixel_clock_MHz > 150000000) ||
+			(pixel_clock_MHz < 60000000)) {
+			bad_pixel_clock_MHz++;
+			continue;
+		}
+		if((horizontal_active_pixels > 1920) ||
+			(horizontal_active_pixels < 1280)) {
+			bad_horizontal_active_pixels_value++;
+			continue;
+		}
+		if((vertical_active_lines > 1080) ||
+			(vertical_active_lines < 720)) {
+			bad_vertical_active_lines_value++;
+			continue;
+		}
+		if(interlaced != 0) {
+			bad_interlaced_value++;
+			continue;
+		}
+		valid_timing_configuration_located = 1;
+		break;
+	}
+
+post_EDID_evaluation:
+
+	/* if no valid timing is found, then set 1024x768 default */
+	setenv(HDMI_STATUS_ENV, "evaluate timing configuration");
+	milestones |= 0x01 << 12;
+	if(valid_timing_configuration_located == 0) {
+		print_str = "no valid timing found, setting 1024x768 default";
+		printf("%s%s\n", WARN_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+
+		pixel_clock_MHz = 65000000;
+		horizontal_active_pixels = 1024;
+		horizontal_blanking_pixels = 320;
+		vertical_active_lines = 768;
+		vertical_blanking_lines = 38;
+		horizontal_sync_offset = 24;
+		horizontal_sync_width = 136;
+		vertical_sync_offset = 3;
+		vertical_sync_width = 6;
+		interlaced = 0;
+	}
+
+	/* determine the aspect ratio of the timing parameters */
+	aspect_ratio = (horizontal_active_pixels * 9) -
+			(vertical_active_lines * 16);
+
+	if(abs(aspect_ratio) > (horizontal_active_pixels * 2))
+		for(i = 0 ;
+			i < (int)(sizeof(init_config_array) /
+				sizeof(init_config)) ;
+			i++)
+			if(init_config_array[i].addr == 0x17)
+				/* set to 4:3 aspect */
+				init_config_array[i].value = 0x00;
+
+	/* calculate the PLL reconfiguration register values */
+	shared_struct.desired_frequency = pixel_clock_MHz;
+	shared_struct.m_value = 0;
+	shared_struct.c_value = 0;
+	shared_struct.k_value = 0;
+	pll_calc_fixed(&shared_struct);
+
+	if(shared_struct.desired_frequency == 0) {
+		print_str = "PLL calculation failure";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(11);
+	}
+
+	/* stop the FBR and wait for it to idle */
+	setenv(HDMI_STATUS_ENV, "stop FBR");
+	milestones |= 0x01 << 13;
+	fbr_ptr[FBR_CNTL_REG] = 0x00000000;
+
+	for(i = 0 ; i < 100000 ; i++) {
+		if((fbr_ptr[FBR_STAT_REG] & 0x01) == 0x00)
+			break;
+	}
+	if(i >= 100000) {
+		print_str = "FBR stop timeout";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(12);
+	}
+
+	/* stop the CVO */
+	setenv(HDMI_STATUS_ENV, "stop CVO");
+	milestones |= 0x01 << 14;
+	cvo_ptr[CVO_CNTL_REG] = 0x00000000;
+
+	for(i = 0 ; i < 100000 ; i++) {
+		if((cvo_ptr[CVO_STAT_REG] & 0x01) == 0x00)
+			break;
+	}
+	if(i >= 100000) {
+		print_str = "CVO stop timeout";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(13);
+	}
+
+	/* assert CVO reset */
+	setenv(HDMI_STATUS_ENV, "assert CVO reset");
+	milestones |= 0x01 << 15;
+	cvo_reset_pio_ptr[0] = 0x00000001;
+
+	/* apply PLL reconfiguration */
+	N_reg = 0x00010000;
+
+	if((shared_struct.m_value & 0x01) != 0) {
+		M_reg = (1 << 17) |
+			(((shared_struct.m_value >> 1) + 1) << 8) |
+			(shared_struct.m_value >> 1);
+	} else {
+		M_reg = ((shared_struct.m_value >> 1) << 8) |
+			(shared_struct.m_value >> 1);
+	}
+
+	if((shared_struct.c_value & 0x01) != 0) {
+		C_reg = (1 << 17) |
+			(((shared_struct.c_value >> 1) + 1) << 8) |
+			(shared_struct.c_value >> 1);
+	} else {
+		C_reg = ((shared_struct.c_value >> 1) << 8) |
+			(shared_struct.c_value >> 1);
+	}
+
+	K_reg = shared_struct.k_value;
+
+	if((shared_struct.m_value == 12) || (shared_struct.m_value == 13)) {
+		BW_reg = 8;
+		CP_reg = 3;
+	} else {
+		BW_reg = 7;
+		CP_reg = 2;
+	}
+
+	VCODIV_reg = 0x00000000;
+
+        pll_ptr[PLL_MODE_REG]	= 0x00;		/* waitrequest mode */
+        pll_ptr[PLL_N_CNTR_REG] = N_reg;
+        pll_ptr[PLL_M_CNTR_REG] = M_reg;
+        pll_ptr[PLL_C_CNTR_REG] = C_reg;
+        pll_ptr[PLL_K_REG]      = K_reg;
+        pll_ptr[PLL_BW_REG]     = BW_reg;
+        pll_ptr[PLL_CP_REG]     = CP_reg;
+        pll_ptr[PLL_VCODIV_REG] = VCODIV_reg;
+
+        pll_ptr[PLL_START_REG] = 0x01;
+
+	/* assert and release PLL reset, then wait for lock */
+	setenv(HDMI_STATUS_ENV, "reset PLL");
+	milestones |= 0x01 << 16;
+	pll_reset_pio_ptr[0] = 0x00000001;
+	pll_reset_pio_ptr[0] = 0x00000000;
+
+	for(i = 0 ; i < 100000 ; i++) {
+		if((pll_locked_pio_ptr[0] & 0x01) == 0x01)
+			break;
+	}
+	if(i >= 100000) {
+		print_str = "PLL lock timeout";
+		printf("%s%s\n", ERROR_STR, print_str);
+		setenv(HDMI_ERROR_ENV, print_str);
+		print_str = "HDMI_milestones";
+		snprintf_buffer_ptr = &snprintf_buffer[0];
+		uitoa(milestones, &snprintf_buffer_ptr);
+		setenv(print_str, snprintf_buffer);
+		printf("%s = %s\n", print_str, snprintf_buffer);
+		return(14);
+	}
+
+	/* release CVO reset */
+	setenv(HDMI_STATUS_ENV, "release CVO reset");
+	milestones |= 0x01 << 17;
+	cvo_reset_pio_ptr[0] = 0x00000000;
+
+	/* configure the FBR */
+	fbr_ptr[FBR_FRM_INFO_REG] = (horizontal_active_pixels << 13) |
+					vertical_active_lines;
+	fbr_ptr[FBR_MISC_REG] = 0x00000000;
+	fbr_ptr[FBR_FRM_STRT_ADDR_REG] = VIDEO_BUFFER;
+
+	/* start the FBR */
+	fbr_ptr[FBR_CNTL_REG] = 0x00000001;
+
+	/* start the CVO */
+	cvo_ptr[CVO_CNTL_REG] = 0x00000007;
+
+	/* configure the CVO */
+	cvo_ptr[CVO_BANK_SELECT_REG]		= 0;
+	cvo_ptr[CVO_M_VALID_REG]		= 0;
+	cvo_ptr[CVO_M_CNTL_REG]			= 0;
+	cvo_ptr[CVO_M_SMPL_CNT_REG]		= horizontal_active_pixels;
+	cvo_ptr[CVO_M_F0_LN_CNT_REG]		= vertical_active_lines;
+	cvo_ptr[CVO_M_F1_LN_CNT_REG]		= 0;
+	cvo_ptr[CVO_M_HOR_FRNT_PRCH_REG]	= horizontal_sync_offset;
+	cvo_ptr[CVO_M_HOR_SYNC_LEN_REG]		= horizontal_sync_width;
+	cvo_ptr[CVO_M_HOR_BLNK_REG]		= horizontal_blanking_pixels;
+	cvo_ptr[CVO_M_VER_FRNT_PRCH_REG]	= vertical_sync_offset;
+	cvo_ptr[CVO_M_VER_SYNC_LEN_REG]		= vertical_sync_width;
+	cvo_ptr[CVO_M_VER_BLNK_REG]		= vertical_blanking_lines;
+	cvo_ptr[CVO_M_F0_VER_F_PRCH_REG]	= 0;
+	cvo_ptr[CVO_M_F0_VER_SYNC_REG]		= 0;
+	cvo_ptr[CVO_M_F0_VER_BLNK_REG]		= 0;
+	cvo_ptr[CVO_M_ACT_PIC_LINE_REG]		= 0;
+	cvo_ptr[CVO_M_F0_VER_RIS_REG]		= 0;
+	cvo_ptr[CVO_M_FLD_RIS_REG]		= 0;
+	cvo_ptr[CVO_M_FLD_FLL_REG]		= 0;
+	cvo_ptr[CVO_M_STNDRD_REG]		= 0;
+	cvo_ptr[CVO_M_SOF_SMPL_REG]		= 0;
+	cvo_ptr[CVO_M_SOF_LINE_REG]		= 0;
+	cvo_ptr[CVO_M_VCOCLK_DIV_REG]		= 0;
+	cvo_ptr[CVO_M_ANC_LINE_REG]		= 0;
+	cvo_ptr[CVO_M_F0_ANC_LINE_REG]		= 0;
+	cvo_ptr[CVO_M_H_SYNC_POL_REG]		= 1;
+	cvo_ptr[CVO_M_V_SYNC_POL_REG]		= 1;
+	cvo_ptr[CVO_M_VALID_REG]		= 1;
+
+	/* configure the ADV7513 */
+	setenv(HDMI_STATUS_ENV, "configure ADV7513");
+	milestones |= 0x01 << 18;
+	for(i = 0 ; i < (int)(sizeof(init_config_array) / sizeof(init_config))
+			; i++) {
+
+		result = i2c_write(
+			ADV7513_MAIN_ADDR,		// uint8_t chip
+			init_config_array[i].addr,	// unsigned int addr
+			1,				// int alen
+			&init_config_array[i].value,	// uint8_t *buffer
+			1				// int len
+		);
+
+		if(result != 0) {
+			print_str = "writing I2C";
+			printf("%s%s\n", ERROR_STR, print_str);
+			setenv(HDMI_ERROR_ENV, print_str);
+			print_str = "HDMI_milestones";
+			snprintf_buffer_ptr = &snprintf_buffer[0];
+			uitoa(milestones, &snprintf_buffer_ptr);
+			setenv(print_str, snprintf_buffer);
+			printf("%s = %s\n", print_str, snprintf_buffer);
+			return(15);
+		}
+	}
+
+	/* report configuration details to u-boot environment */
+	setenv(HDMI_STATUS_ENV, "report configuration");
+	milestones |= 0x01 << 19;
+
+	print_str = "HDMI_vld_tmng_fnd";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(valid_timing_configuration_located, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_h_active_pix";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(horizontal_active_pixels, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_h_blank_pix";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(horizontal_blanking_pixels, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_h_sync_off";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(horizontal_sync_offset, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_h_sync_width";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(horizontal_sync_width, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_v_active_lin";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(vertical_active_lines, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_v_blank_lin";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(vertical_blanking_lines, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_v_sync_off";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(vertical_sync_offset, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_v_sync_width";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(vertical_sync_width, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_pll_freq";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(shared_struct.desired_frequency, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_pll_m";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(shared_struct.m_value, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_pll_c";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(shared_struct.c_value, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_pll_k";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(shared_struct.k_value, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_stride";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(horizontal_active_pixels * 4, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	print_str = "HDMI_milestones";
+	snprintf_buffer_ptr = &snprintf_buffer[0];
+	uitoa(milestones, &snprintf_buffer_ptr);
+	setenv(print_str, snprintf_buffer);
+	printf("%s = %s\n", print_str, snprintf_buffer);
+
+	setenv(HDMI_STATUS_ENV, "complete");
+
+	/* paint the frame buffer */
+/*
+	for(j = 0 ; j < vertical_active_lines ; j++) {
+		for(i = 0 ; i < horizontal_active_pixels ; i++) {
+			if(i < (horizontal_active_pixels / 3))
+				video_ptr[i + (j * horizontal_active_pixels)] =
+								0x00FF0000;
+			else if(i < (2 * (horizontal_active_pixels / 3)))
+				video_ptr[i + (j * horizontal_active_pixels)] =
+								0x0000FF00;
+			else
+				video_ptr[i + (j * horizontal_active_pixels)] =
+								0x000000FF;
+		}
+	}
+
+	for(i = 0 ; i < horizontal_active_pixels ; i++) {
+		video_ptr[i] = 0x00FFFFFF;
+		video_ptr[i + (horizontal_active_pixels *
+				(vertical_active_lines - 1))] = 0x00FFFFFF;
+	}
+
+	for(i = 0 ; i < vertical_active_lines ; i++) {
+		video_ptr[i * horizontal_active_pixels] = 0x00FFFFFF;
+		video_ptr[(horizontal_active_pixels - 1) +
+				(i * horizontal_active_pixels)] =
+							0x00FFFFFF;
+	}
+*/
+	for(j = 0 ; j < vertical_active_lines ; j++) {
+		for(i = 0 ; i < horizontal_active_pixels ; i++) {
+			video_ptr[i + (j * horizontal_active_pixels)] =
+								0x000071C5;
+		}
+	}
+
+	print_str = "background painted";
+	setenv(HDMI_INFO_ENV, print_str);
+
+	bmp_header_ptr = (struct bmp_image_header *)BMP_IMAGE_BASE;
+
+	if(bmp_header_ptr->bmp_signature != 0x4D42) {
+		print_str = "bad BMP signature";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(16);
+	}
+
+	if(bmp_header_ptr->bmp_header_size != 124) {
+		print_str = "bad BMP header size";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(17);
+	}
+
+	if(bmp_header_ptr->bmp_color_planes != 1) {
+		print_str = "bad BMP color planes";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(18);
+	}
+
+	if(bmp_header_ptr->bmp_bits_per_pixel != 24) {
+		print_str = "bad BMP bits per pixel";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(19);
+	}
+
+	if(bmp_header_ptr->bmp_compression_method != 0) {
+		print_str = "bad BMP compression method";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(20);
+	}
+
+	if(bmp_header_ptr->bmp_width != 640) {
+		print_str = "bad BMP image width";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(21);
+	}
+
+	if(bmp_header_ptr->bmp_height != 480) {
+		print_str = "bad BMP image height";
+		printf("%s%s\n", INFO_STR, print_str);
+		setenv(HDMI_INFO_ENV, print_str);
+		return(22);
+	}
+
+	bmp_pixel_ptr = (struct bmp_24_bit_pixel *)(BMP_IMAGE_BASE +
+					bmp_header_ptr->bmp_bitmap_offset);
+
+	for(j = bmp_header_ptr->bmp_height - 1 ; j >= 0 ; j--) {
+		for(i = 0 ; i < bmp_header_ptr->bmp_width ; i++) {
+			video_ptr[i + ((((bmp_header_ptr->bmp_height - 1) - j +
+					((vertical_active_lines / 2) -
+					(bmp_header_ptr->bmp_height / 2))) *
+						horizontal_active_pixels)) +
+					((horizontal_active_pixels / 2) -
+					(bmp_header_ptr->bmp_width / 2)) ] =
+				(bmp_pixel_ptr[i +
+					(j * bmp_header_ptr->bmp_width)].red
+									<< 0) |
+				(bmp_pixel_ptr[i +
+					(j * bmp_header_ptr->bmp_width)].green
+									<< 8) |
+				(bmp_pixel_ptr[i +
+					(j * bmp_header_ptr->bmp_width)].blue
+									<< 16);
+		}
+	}
+
+	print_str = "startup BMP painted";
+	setenv(HDMI_INFO_ENV, print_str);
+	return (0);
+}
+
+/*
+Original comment from pll_calc_float and pll_calc_fixed model test program. We
+are only implementing the fixed  point version here.
+
+Our goal here is to calculate the M, C and K values for the Altera fractional
+PLL implemented very simply with one output.  The PLL input reference frequency
+is multiplied by M.K to create an internal VCO frequency.  The M counter
+represents the integer divide value and the K counter represents the fractional
+divide value.  That VCO frequency is then divided by the C counter to produce
+the desired output frequency.  The VCO frequency must be set to something
+between 400MHz and 1300MHz. and we must choose a K value that is between 0.05
+and 0.95.  A K value of 0.00 is also acceptable.
+
+Our algorithm will begin by locating the minimum C divider that allows us to
+set a VCO frequency above 400MHz.  From that C we derive the M.K value from the
+product of C and the input and output clock ratio.  The M.K value must not
+cause a VCO frequency above 1300MHz otherwise it is rejected.  The K value is
+checked to be either 0.0 or fall between 0.05 and 0.95.  If all of these
+conditions are not met, then we increment our C value by one, and we evaluate
+the new M.K value until we find parameters that are all legal.  If we cannot
+locate a set of valid parameters we increase the desired clock frequency by
+10KHz and we re-run the algorithm.
+
+We expect the desired output frequency of the PLL to be limited to a
+maximum of MAX_INPUT_FREQ and a minimum of MIN_INPUT_FREQ.
+
+The PLL reference input clock frequency is PLL_REF_FREQ.
+
+The PLL VCO frequency must remain between maximum and minimum limits.
+MAX_VCO = (MAX_MULTIPLE * PLL_REF_FREQ)
+MIN_VCO = (MIN_MULTIPLE * PLL_REF_FREQ)
+
+Here is some pseudo code of how the algorithm is implemented:
+
+We would like to locate a starting C value so we start by computing the ratio
+of the desired output clock frequency and the PLL reference input clock
+frequency:
+
+	clock_ratio = desired_frequency / PLL_REF_FREQ
+
+Next we multiply the minimum multiplier by the clock ratio to get the minimum C
+value that will not exceed a 400MHz VCO frequency:
+
+	min_div_ratio = MIN_MULTIPLE / clock_ratio
+
+Now this C value is likely a real number with some fractional quantity, but the
+C counter must be an integer so we must ceil() this number to get the next
+higher integer value:
+
+	ceil_min_div_ratio = ceil(min_div_ratio)
+
+Now we can calculate the M.K value that we would get with this C value:
+
+	product_ratio_ceil = clock_ratio * ceil_min_div_ratio
+
+Now we start to evaluate the K value by taking the floor of the M.K value which
+is M:
+
+	floor_product = (unsigned long)(product_ratio_ceil)
+
+And we can extract the K value by computing the difference of M.K - M:
+
+	fraction = product_ratio_ceil - floor_product
+
+Now we can evaluate the value of K:
+	if ((fraction >= 0.05) && (fraction <= 0.95))
+		break;
+	if (fraction == 0.0)
+		break;
+
+If K is not valid, then we increment the value of C and compute a new M.K value:
+
+	ceil_min_div_ratio++
+	product_ratio_ceil = clock_ratio * ceil_min_div_ratio
+
+If we were unable to locate a valid set of M, C, and K, then we loop back
+through the algorithm with a new desired frequency that is 10KHz greater than
+the one we just failed to evaluate:
+
+	if (product_ratio_ceil >= MAX_MULTIPLE)
+		continue;
+	else
+		break;
+
+NOTE: in this example below, we create a floating point implementation where the
+values that we work with are real double precision floating point values.  We
+also create a fixed point implementation below where the values that we work
+with are scaled to a fixed point number where 1.0 is represented by
+0x0000_0001_0000_0000 or 2**32, such that there is a 32-bit integer value and
+a 32-bit fractional value.
+
+One additional requirement beyond the values for M, C, and K, are the settings
+bandwidth register and the CP register.  These appear to be set to 7 and 2
+respectively, except for values of M that set the VCO into the range of 600MHz
+to 700MHz.  In our case here, that would mean that M values of 12 ant 13 for
+VCO frequencies of 600MHz and 650MHz would be affected.
+
+        if((m_value == 12) || (m_value == 13)) {
+                BW_reg = 8;
+                CP_reg = 3;
+        } else {
+                BW_reg = 7;
+                CP_reg = 2;
+        }
+*/
+
+void pll_calc_fixed(struct pll_calc_struct *the_pll_calc_struct) {
+
+	int i;
+	unsigned long long input_freq;
+	unsigned long long clock_ratio;
+	unsigned long long min_div_ratio;
+	unsigned long long ceil_min_div_ratio;
+	unsigned long long product_ratio_ceil;
+	unsigned long long floor_product;
+	unsigned long long fraction;
+	unsigned long long k_value;
+
+	/*
+	calculate the PLL reconfiguration register values
+
+	There are some frequencies that we cannot fit into valid M, C and K
+	values, so we setup a loop that will sweep through 1MHz in 10KHz
+	increments.  Most dead zones are no greater than 100KHz, so we should be
+	able to find a frequency reasonably close to the desired frequency.
+	*/
+	for (i = 0; i < 100; i++) {
+		/*
+		increment the desired frequency by 10KHz each pass through
+		the loop since there are some frequencies that we cannot fit
+		into the valid ranges for all parameters
+		*/
+		input_freq = the_pll_calc_struct->desired_frequency +
+								(i * 10000);
+
+		/* calculate a candidate C value */
+		clock_ratio = (input_freq << 32) / PLL_REF_FREQ;
+		if (clock_ratio == 0) {
+			the_pll_calc_struct->desired_frequency = 0;
+			the_pll_calc_struct->m_value = 1;
+			return;
+		}
+		min_div_ratio = ((unsigned long long)MIN_MULTIPLE << 32) /
+								clock_ratio;
+
+		/* calculate ceil for the integer value of C */
+		ceil_min_div_ratio = min_div_ratio;
+		/*
+		use rounding fuzz of 3 to align better with float point model
+		*/
+		if((((unsigned long long)MIN_MULTIPLE << 32) % clock_ratio) > 3)
+			ceil_min_div_ratio += 1;
+
+		/* calculate the M.K value based on our proposed C value */
+		product_ratio_ceil = clock_ratio * ceil_min_div_ratio;
+
+		/*
+		evaluate K, if invalid, increment C and re-evaluate, continue
+		incrementing C until M.K exceeds MAX_MULTIPLE
+		*/
+		fraction = 0;
+		floor_product = 0;
+		while(product_ratio_ceil <
+			((unsigned long long)MAX_MULTIPLE << 32)) {
+			/* extract M */
+			floor_product = product_ratio_ceil &
+				0xFFFFFFFF00000000ULL;
+
+			/* extract K */
+			fraction = product_ratio_ceil - floor_product;
+
+			/* evaluate K */
+			/*
+			use rounding fuzz of 3 to align better with float point
+			model
+			*/
+			if ((fraction >= (FXD_PNT_0P05 - 3)) &&
+				(fraction <= (FXD_PNT_0P95 + 0)))
+				break;
+			/*
+			use rounding fuzz of 4 to align better with float point
+			model
+			*/
+			if (fraction <= 4) {
+				fraction = 0;
+				break;
+			}
+			/*
+			use rounding fuzz of -4 to align better with float point
+			model
+			*/
+			if (fraction >= 0xFFFFFFFC) {
+				fraction = 0;
+				floor_product += FXD_PNT_1P0;
+				break;
+			}
+
+			/* increment C */
+			ceil_min_div_ratio++;
+
+			/* new M.K value */
+			product_ratio_ceil = clock_ratio * ceil_min_div_ratio;
+		}
+
+		/* if we did not locate a valid M.K value, the try again */
+		if (product_ratio_ceil >=
+			((unsigned long long)MAX_MULTIPLE << 32))
+			continue;
+		else
+			break;
+	}
+
+	/*
+	this should never occur but if we were unable to locate a valid
+	configuration, we return with an desired frequency of ZERO
+	*/
+	if (i >= 100) {
+		the_pll_calc_struct->desired_frequency = 0;
+		return;
+	}
+
+	/*
+	if the K value is ZERO then we return 1
+	otherwise we return K * 2^32
+	*/
+	if (fraction == 0)
+		k_value = 1;
+	else
+		k_value = fraction;
+
+	/* return all computed values */
+	the_pll_calc_struct->desired_frequency = input_freq;
+	the_pll_calc_struct->m_value = (unsigned long)(floor_product >> 32);
+	the_pll_calc_struct->c_value = (unsigned long)ceil_min_div_ratio;
+	the_pll_calc_struct->k_value = (unsigned long)k_value;
+
+	return;
+}
+
+/* unsigned integer to ascii */
+const char digits[] = "0123456789";
+void uitoa(uint32_t uint32_input, char **output_str) {
+
+	char *str_ptr;
+
+	if(uint32_input > 9) {
+		uitoa(uint32_input / 10, output_str);
+		uint32_input -= (uint32_input / 10) * 10;
+	}
+
+	str_ptr = *output_str;
+	*str_ptr++ = digits[uint32_input];
+	*str_ptr = '\0';
+	*output_str = str_ptr;
+}
+
+/*
+create a hang symbol because the _div0 library will want to call it, our code
+above should not execute division by zero
+*/
+void hang(void) {
+	printf("HANG!!!\n");
+	while(1);
+}
+
diff --git a/examples/standalone/de10_nano_hdmi_config.h b/examples/standalone/de10_nano_hdmi_config.h
new file mode 100644
index 0000000..140b1ce
--- /dev/null
+++ b/examples/standalone/de10_nano_hdmi_config.h
@@ -0,0 +1,195 @@
+/*
+ * The MIT License (MIT)
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _DE10_NANO_HDMI_CONFIG_H_
+#define _DE10_NANO_HDMI_CONFIG_H_
+
+/* PLL calculation parameters */
+#define PLL_REF_FREQ	(50000000)
+#define MAX_INPUT_FREQ	(165000000)
+#define MIN_INPUT_FREQ	(65000000)
+#define MIN_MULTIPLE	(8)
+#define MAX_MULTIPLE	(26)
+
+/* macros for fixed point algorithm */
+#define FXD_PNT_1P0	(1ULL << 32)
+#define FXD_PNT_0P95	((unsigned long long)(FXD_PNT_1P0 * 0.95))
+#define FXD_PNT_0P05	((unsigned long long)(FXD_PNT_1P0 * 0.05))
+
+/* pll calc function data structure */
+struct pll_calc_struct {
+	unsigned long desired_frequency;
+	unsigned long m_value;
+	unsigned long c_value;
+	unsigned long k_value;
+};
+
+/* LWH2F bridge */
+#define LWH2F_BASE	(0xFF200000)
+
+/* SYSID component */
+#define SYSID_BASE	(0x1000)
+#define SYSID_ID	2899645442
+#define SYSID_TS	1485984203
+
+/* CVO RESET PIO component */
+#define CVO_RESET_PIO_BASE	(0xC000)
+
+/* PLL LOCKED PIO component */
+#define PLL_LOCKED_PIO_BASE	(0xD000)
+
+/* PLL RESET PIO component */
+#define PLL_RESET_PIO_BASE	(0xB000)
+
+/* CVO component */
+#define CVO_BASE		(0x9000)
+#define CVO_CNTL_REG		(0)
+#define CVO_STAT_REG		(1)
+#define CVO_INTR_REG		(2)
+#define CVO_VID_MODE_MATCH_REG	(3)
+#define CVO_BANK_SELECT_REG	(4)
+#define CVO_M_CNTL_REG		(5)
+#define CVO_M_SMPL_CNT_REG	(6)
+#define CVO_M_F0_LN_CNT_REG	(7)
+#define CVO_M_F1_LN_CNT_REG	(8)
+#define CVO_M_HOR_FRNT_PRCH_REG	(9)
+#define CVO_M_HOR_SYNC_LEN_REG	(10)
+#define CVO_M_HOR_BLNK_REG	(11)
+#define CVO_M_VER_FRNT_PRCH_REG	(12)
+#define CVO_M_VER_SYNC_LEN_REG	(13)
+#define CVO_M_VER_BLNK_REG	(14)
+#define CVO_M_F0_VER_F_PRCH_REG	(15)
+#define CVO_M_F0_VER_SYNC_REG	(16)
+#define CVO_M_F0_VER_BLNK_REG	(17)
+#define CVO_M_ACT_PIC_LINE_REG	(18)
+#define CVO_M_F0_VER_RIS_REG	(19)
+#define CVO_M_FLD_RIS_REG	(20)
+#define CVO_M_FLD_FLL_REG	(21)
+#define CVO_M_STNDRD_REG	(22)
+#define CVO_M_SOF_SMPL_REG	(23)
+#define CVO_M_SOF_LINE_REG	(24)
+#define CVO_M_VCOCLK_DIV_REG	(25)
+#define CVO_M_ANC_LINE_REG	(26)
+#define CVO_M_F0_ANC_LINE_REG	(27)
+#define CVO_M_H_SYNC_POL_REG	(28)
+#define CVO_M_V_SYNC_POL_REG	(29)
+#define CVO_M_VALID_REG		(30)
+
+/* FBR component */
+#define FBR_BASE		(0x8000)
+#define FBR_CNTL_REG		(0)
+#define FBR_STAT_REG		(1)
+#define FBR_INTR_REG		(2)
+#define FBR_FRM_CNTR_REG	(3)
+#define FBR_REP_CNTR_REG	(4)
+#define FBR_FRM_INFO_REG	(5)
+#define FBR_FRM_STRT_ADDR_REG	(6)
+#define FBR_FRM_RDR_REG		(7)
+#define FBR_MISC_REG		(8)
+
+/* video buffer in system DRAM */
+#define VIDEO_BUFFER	(0x3F000000)
+
+/* PLL calculation parameters */
+#define PLL_REF_FREQ	(50000000)
+#define MAX_INPUT_FREQ	(165000000)
+#define MIN_INPUT_FREQ	(65000000)
+#define MIN_MULTIPLE	(8)
+#define MAX_MULTIPLE	(26)
+#define X_WIDTH		(32)
+
+/* PLL RECONFIG component */
+#define PLL_RECNFG_BASE	(0xA000)
+#define PLL_MODE_REG	(0)
+#define PLL_STATUS_REG	(1)
+#define PLL_START_REG	(2)
+#define PLL_N_CNTR_REG	(3)
+#define PLL_M_CNTR_REG	(4)
+#define PLL_C_CNTR_REG	(5)
+#define PLL_DPS_REG	(6)
+#define PLL_K_REG	(7)
+#define PLL_BW_REG	(8)
+#define PLL_CP_REG	(9)
+#define PLL_C0_RD_REG	(10)
+#define PLL_VCODIV_REG	(0x1C)
+
+/* ADV7513 component */
+#define I2C_BUS "/dev/i2c-1"
+#define ADV7513_MAIN_ADDR       (0x39)
+#define ADV7513_EDID_ADDR       (0x3F)
+#define ADV7513_CHIP_ID_HI      (0xF5)
+#define ADV7513_CHIP_ID_HI_VAL  (0x75)
+#define ADV7513_CHIP_ID_LO      (0xF6)
+#define ADV7513_CHIP_ID_LO_VAL  (0x11)
+#define ADV7513_PWR_DWN         (0x41)
+#define ADV7513_PWR_DWN_BIT     (0x40)
+#define ADV7513_HPD_MNSNS       (0x42)
+#define ADV7513_HPD_MNSNS_BITS  (0x60)
+#define ADV7513_EDID_RDY        (0x96)
+#define ADV7513_EDID_RDY_BIT    (0x04)
+#define ADV7513_HPD_CNTL        (0xD6)
+#define ADV7513_HPD_CNTL_BITS   (0xC0)
+
+typedef struct {
+	u_char addr;
+	u_char value;
+} init_config;
+
+/* common use strings */
+#define ERROR_STR	"HDMI ERROR: "
+#define WARN_STR	"HDMI WARNING: "
+#define INFO_STR	"HDMI INFO: "
+#define HDMI_STATUS_ENV	"HDMI_status"
+#define HDMI_ERROR_ENV	"HDMI_error"
+#define HDMI_INFO_ENV	"HDMI_info"
+
+/* BMP image support */
+#define BMP_IMAGE_BASE	(0x0C300000)
+struct bmp_image_header {
+	uint16_t bmp_signature;
+	uint32_t bmp_file_size;
+	uint16_t bmp_reserved_1;
+	uint16_t bmp_reserved_2;
+	uint32_t bmp_bitmap_offset;
+
+	uint32_t bmp_header_size;
+	uint32_t bmp_width;
+	uint32_t bmp_height;
+	uint16_t bmp_color_planes;
+	uint16_t bmp_bits_per_pixel;
+	uint32_t bmp_compression_method;
+	uint32_t bmp_image_size;
+	uint32_t bmp_h_resolution;
+	uint32_t bmp_v_resolution;
+	uint32_t bmp_number_of_colors;
+	uint32_t bmp_important_colors;
+} __attribute__((packed));
+
+struct bmp_24_bit_pixel {
+	uint8_t red;
+	uint8_t green;
+	uint8_t blue;
+};
+
+#endif /* _DE10_NANO_HDMI_CONFIG_H_ */
+
diff --git a/examples/standalone/dump_adv7513_edid.c b/examples/standalone/dump_adv7513_edid.c
new file mode 100644
index 0000000..0644e06
--- /dev/null
+++ b/examples/standalone/dump_adv7513_edid.c
@@ -0,0 +1,697 @@
+/*                                                                               
+ * The MIT License (MIT)                                                        
+ * Copyright (c) 2016 Intel Corporation                                         
+ *                                                                              
+ * Permission is hereby granted, free of charge, to any person obtaining a copy  
+ * of this software and associated documentation files (the "Software"), to deal 
+ * in the Software without restriction, including without limitation the rights  
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     
+ * copies of the Software, and to permit persons to whom the Software is         
+ * furnished to do so, subject to the following conditions:                      
+ *                                                                              
+ * The above copyright notice and this permission notice shall be included in    
+ * all copies or substantial portions of the Software.                           
+ *                                                                               
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     
+ * THE SOFTWARE.                                                                 
+ */
+
+#include <common.h>
+#include <exports.h>
+#include "de10_nano_hdmi_config.h"
+
+struct legacy_timing_mode {
+	u_int columns;
+	u_int rows;
+	u_int rate;
+	const char *description;
+};
+
+struct legacy_timing_mode legacy_support_array[24] = {
+/* EDID byte 35 */
+/* bit 7 */ { 720,  400, 70, "720x400 @ 70 Hz"},
+/* bit 6 */ { 720,  400, 88, "720x400 @ 88 Hz"},
+/* bit 5 */ { 640,  480, 60, "640x480 @ 60 Hz"},
+/* bit 4 */ { 640,  480, 67, "640x480 @ 67 Hz"},
+/* bit 3 */ { 640,  480, 72, "640x480 @ 72 Hz"},
+/* bit 2 */ { 640,  480, 75, "640x480 @ 75 Hz"},
+/* bit 1 */ { 800,  600, 56, "800x600 @ 56 Hz"},
+/* bit 0 */ { 800,  600, 60, "800x600 @ 60 Hz"},
+/* EDID byte 36 */
+/* bit 7 */ { 800,  600, 72, "800x600 @ 72 Hz"},
+/* bit 6 */ { 800,  600, 75, "800x600 @ 75 Hz"},
+/* bit 5 */ { 832,  624, 75, "832x624 @ 75 Hz"},
+/* bit 4 */ {1024,  768, 87, "1024x768 @ 87 Hz, interlaced (1024x768i)"},
+/* bit 3 */ {1024,  768, 60, "1024x768 @ 60 Hz"},
+/* bit 2 */ {1024,  768, 72, "1024x768 @ 72 Hz"},
+/* bit 1 */ {1024,  768, 75, "1024x768 @ 75 Hz"},
+/* bit 0 */ {1280, 1024, 75, "1280x1024 @ 75 Hz"},
+/* EDID byte 38 */
+/* bit 7 */ {1152,  870, 75, "1152x870 @ 75 Hz (Apple Macintosh II)"},
+/* bit 6 */ {   0,    0,  0, "manufacturer specific 6"},
+/* bit 5 */ {   0,    0,  0, "manufacturer specific 5"},
+/* bit 4 */ {   0,    0,  0, "manufacturer specific 4"},
+/* bit 3 */ {   0,    0,  0, "manufacturer specific 3"},
+/* bit 2 */ {   0,    0,  0, "manufacturer specific 2"},
+/* bit 1 */ {   0,    0,  0, "manufacturer specific 1"},
+/* bit 0 */ {   0,    0,  0, "manufacturer specific 0"},
+};
+
+/* test program */
+int dump_adv7513_edid(int argc, char * const argv[]) {
+
+	int i;
+	int j;
+	int result;
+	char *print_str;
+	uint8_t adv7513_read_buffer[256];
+	uint8_t adv7513_edid_buffer[256];
+	uint8_t adv7513_write_val;
+	uint8_t checksum;
+	uint8_t edid_header_pattern_array[8] = {
+			0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
+	uint8_t *descriptor_base;
+
+	char descriptor_text[14];
+	uint8_t dtd_offset;
+	uint8_t dtd_count;
+
+	char manufacturer_letter[3];
+	uint16_t manufacturer_product_code;
+	uint32_t serial_number;
+	uint8_t manufacture_week;
+	uint8_t manufacture_year;
+	uint8_t manufacture_version;
+	uint8_t manufacture_version_dot;
+	uint32_t legacy_bitmap;
+	const char *aspect_str;
+
+	uint16_t pixel_clock;
+	uint16_t horizontal_active_pixels;
+	uint16_t horizontal_blanking_pixels;
+	uint16_t vertical_active_lines;
+	uint16_t vertical_blanking_lines;
+	uint16_t horizontal_sync_offset;
+	uint16_t horizontal_sync_width;
+	uint8_t vertical_sync_offset;
+	uint8_t vertical_sync_width;
+	uint8_t horizontal_border_pixels;
+	uint8_t vertical_border_pixels;
+	uint8_t interlaced;
+
+	/* initialize u-boot application environment */
+	app_startup(argv);
+
+	/* since we cannot read a single register from the ADV7513 with the
+	 * default I2C driver, we just read the first N registers starting
+	 * from ZERO and reaching up to the register we want
+	 */
+	print_str = "read ADV7513 chip ID";
+	printf("%s%s\n", INFO_STR, print_str);
+	result = i2c_read(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			0x00,			// unsigned int addr
+			0,			// int alen
+			adv7513_read_buffer,	// uint8_t *buffer
+			ADV7513_CHIP_ID_LO + 1	// int len
+		);
+
+	if(result != 0) {
+		print_str = "reading I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(1);
+	}
+
+	/* verify the chip id for the ADV7513 */
+	if(
+		(adv7513_read_buffer[ADV7513_CHIP_ID_HI] !=
+			ADV7513_CHIP_ID_HI_VAL) ||
+		(adv7513_read_buffer[ADV7513_CHIP_ID_LO] !=
+			ADV7513_CHIP_ID_LO_VAL) ) {
+
+		print_str = "Bad Chip ID";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(2);
+	}
+
+	/* verify that we see a monitor attached to the HDMI connector */
+	if((adv7513_read_buffer[ADV7513_HPD_MNSNS] & ADV7513_HPD_MNSNS_BITS) !=
+		ADV7513_HPD_MNSNS_BITS) {
+		
+		print_str = "No HDMI display detected";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(3);
+	}
+
+	/* power down the ADV7513 */
+	print_str = "power down ADV7513";
+	printf("%s%s\n", INFO_STR, print_str);
+	adv7513_write_val = adv7513_read_buffer[ADV7513_PWR_DWN];
+	adv7513_write_val |= ADV7513_PWR_DWN_BIT;
+	result = i2c_write(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			ADV7513_PWR_DWN,	// unsigned int addr
+			1,			// int alen
+			&adv7513_write_val,	// uint8_t *buffer
+			1			// int len
+		);
+
+	if(result != 0) {
+		print_str = "writing I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(4);
+	}
+
+	/* power up the ADV7513 */
+	print_str = "power up ADV7513";
+	printf("%s%s\n", INFO_STR, print_str);
+	adv7513_write_val &= ~ADV7513_PWR_DWN_BIT;
+	result = i2c_write(
+			ADV7513_MAIN_ADDR,	// uint8_t chip
+			ADV7513_PWR_DWN,	// unsigned int addr
+			1,			// int alen
+			&adv7513_write_val,	// uint8_t *buffer
+			1			// int len
+		);
+
+	if(result != 0) {
+		print_str = "writing I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(5);
+	}
+
+	/* wait for the EDID data to become ready */
+	print_str = "wait EDID ready";
+	printf("%s%s\n", INFO_STR, print_str);
+	for(i = 0 ; i < 1000 ; i++) {
+		result = i2c_read(
+				ADV7513_MAIN_ADDR,	// uint8_t chip
+				0x00,			// unsigned int addr
+				0,			// int alen
+				adv7513_read_buffer,	// uint8_t *buffer
+				ADV7513_EDID_RDY + 1	// int len
+			);
+
+		if(result != 0) {
+			print_str = "reading I2C";
+			printf("%s%s\n", ERROR_STR, print_str);
+			return(6);
+		}
+		
+		if((adv7513_read_buffer[ADV7513_EDID_RDY] &
+			ADV7513_EDID_RDY_BIT) == ADV7513_EDID_RDY_BIT)
+			break;
+	}
+	
+	if(i >= 1000) {
+		print_str = "EDID timeout";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(7);
+	}
+
+	/* read the EDID data */
+	print_str = "read EDID data";
+	printf("%s%s\n", INFO_STR, print_str);
+	result = i2c_read(
+			ADV7513_EDID_ADDR,	// uint8_t chip
+			0x00,			// unsigned int addr
+			0,			// int alen
+			adv7513_edid_buffer,	// uint8_t *buffer
+			256			// int len
+		);
+
+	if(result != 0) {
+		print_str = "reading I2C";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(8);
+	}
+
+	/* print the EDID data */
+	printf("\nRaw EDID Data\n");
+	for(i = 0 ; i < 256 ; i++) {
+		printf("0x%02X ", adv7513_edid_buffer[i]);
+		if(((i + 1) % 16) == 0)
+			printf("\n");
+	}
+
+	/* verify EDID block 0 checksum */
+	checksum = 0;
+	for(i = 0 ; i < 128 ; i++)
+		checksum += adv7513_edid_buffer[i];
+
+	if(checksum != 0) {
+		print_str = "EDID block 0 checksum";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(9);
+	}
+
+	/* verify block 0 header pattern */
+	for(i = 0 ; i < 8 ; i++) {
+		if(edid_header_pattern_array[i] != adv7513_edid_buffer[i]) {
+			print_str = "EDID header pattern";
+			printf("%s%s\n", ERROR_STR, print_str);
+			return(10);
+		}
+	}
+
+	/* extract three letter manufacturer ID */
+	if((adv7513_edid_buffer[8] & 0x80) != 0x00) {
+		print_str = "manufacturer letter format";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(1);
+	}
+
+	manufacturer_letter[0] = adv7513_edid_buffer[8];
+	manufacturer_letter[0] >>= 2;
+	manufacturer_letter[0] -= 1;
+	manufacturer_letter[0] += 'A';
+
+	manufacturer_letter[1] = adv7513_edid_buffer[8];
+	manufacturer_letter[1] &= 0x03;
+	manufacturer_letter[1] <<= 3;
+	manufacturer_letter[1] |= (adv7513_edid_buffer[9] >> 5) & 0x07;
+	manufacturer_letter[1] -= 1;
+	manufacturer_letter[1] += 'A';
+
+	manufacturer_letter[2] = adv7513_edid_buffer[9];
+	manufacturer_letter[2] &= 0x1F;
+	manufacturer_letter[2] -= 1;
+	manufacturer_letter[2] += 'A';
+
+	printf("\nManufacturer ID: %c%c%c\n",
+			manufacturer_letter[0],
+			manufacturer_letter[1],
+			manufacturer_letter[2]);
+
+	/* extract manufacturer product code */
+	manufacturer_product_code = adv7513_edid_buffer[10] |
+					(adv7513_edid_buffer[11] << 8);
+
+	printf("Manufacturer Product Code: 0x%04X\n",
+			manufacturer_product_code);
+
+	/* extract serial number */
+	serial_number = adv7513_edid_buffer[12] |
+		(adv7513_edid_buffer[13] << 8) |
+		(adv7513_edid_buffer[14] << 16) |
+		(adv7513_edid_buffer[15] << 24);
+
+	printf("Serial Number: 0x%08X\n", serial_number);
+
+	/* extract manufacture week */
+	manufacture_week = adv7513_edid_buffer[16];
+
+	printf("Manufacture Week: %u\n", manufacture_week);
+
+	/* extract manufacture year */
+	manufacture_year = adv7513_edid_buffer[17];
+
+	printf("Manufacture Year: %u\n", manufacture_year + 1990);
+
+	/* extract EDID version */
+	manufacture_version = adv7513_edid_buffer[18];
+	manufacture_version_dot = adv7513_edid_buffer[19];
+
+	printf("Manufacture Version: %c.%c\n",
+			manufacture_version + '0',
+			manufacture_version_dot + '0');
+
+	/* decode supported legacy timings */
+	legacy_bitmap = (adv7513_edid_buffer[35] << 16) |
+			(adv7513_edid_buffer[36] <<  8) |
+			(adv7513_edid_buffer[37] <<  0);
+	
+	printf("\nSupported legacy timing modes:\n");
+	for(i = 0 ; i < 24 ; i++) {
+		if(legacy_bitmap & (1 << (23 - i)))
+			printf("%s\n", legacy_support_array[i].description);
+	}
+
+	/* decode standard display modes */
+	printf("\nStandard Display Modes\n");
+
+	for(i = 0 ; i < 8 ; i++) {
+		if(
+			(adv7513_edid_buffer[38 + (i * 2)] == 0x01) &&
+			(adv7513_edid_buffer[39 + (i * 2)] == 0x01)
+		  ) {
+			printf("Entry %d: unused\n", i);
+		} else {
+			switch ((adv7513_edid_buffer[39 + (i * 2)] >> 6) &
+									0x3 ) {
+				case 0x0:
+					aspect_str = "16:10";
+					break;
+				case 0x1:
+					aspect_str = "4:3";
+					break;
+				case 0x2:
+					aspect_str = "5:4";
+					break;
+				case 0x3:
+					aspect_str = "16:9";
+					break;
+				default:
+					aspect_str = "unknown";
+					break;
+			}
+
+			printf("Entry %d: %d pix | %s | %d Hz\n",
+				i,
+				(adv7513_edid_buffer[38 + (i * 2)] + 31) * 8,
+				aspect_str,
+				(adv7513_edid_buffer[39 + (i * 2)] & 0x3F) +
+									60);
+		}
+	}
+
+	/* decode descriptor blocks */
+	for(i = 0 ; i < 4 ; i++) {
+		printf("\nDESCRIPTOR %d\n", i + 1);
+
+		descriptor_base = &adv7513_edid_buffer[54];
+		descriptor_base += i * 18;
+		if(
+			(descriptor_base[0] == 0) &&
+			(descriptor_base[1] == 0) &&
+			(descriptor_base[2] == 0) &&
+			(descriptor_base[4] == 0)
+		  ) {
+			if(descriptor_base[3] == 0xFF) {
+				memset(descriptor_text, '\0', 14);
+				for(j = 0; j < 13 ; j++) {
+					if(
+					(descriptor_base[5 + j] >= 32) &&
+					(descriptor_base[5 + j] <= 126)
+					) {
+						descriptor_text[j] =
+							descriptor_base[5 + j];
+					} else {
+						break;
+					}
+				}
+				printf("Monitor Serial Number: %s\n",
+						descriptor_text);
+			}
+			
+			if(descriptor_base[3] == 0xFE) {
+				memset(descriptor_text, '\0', 14);
+				for(j = 0; j < 13 ; j++) {
+					if(
+					(descriptor_base[5 + j] >= 32) &&
+					(descriptor_base[5 + j] <= 126)
+					) {
+						descriptor_text[j] =
+							descriptor_base[5 + j];
+					} else {
+						break;
+					}
+				}
+				printf("Unspecified Text: %s\n",
+						descriptor_text);
+			}
+			
+			if(descriptor_base[3] == 0xFD)
+				printf("Monitor Range Limits\n");
+			
+			if(descriptor_base[3] == 0xFC) {
+				memset(descriptor_text, '\0', 14);
+				for(j = 0; j < 13 ; j++) {
+					if(
+					(descriptor_base[5 + j] >= 32) &&
+					(descriptor_base[5 + j] <= 126)
+					) {
+						descriptor_text[j] =
+							descriptor_base[5 + j];
+					} else {
+						break;
+					}
+				}
+				printf("Monitor Name: %s\n", descriptor_text);
+			}
+			
+			if(descriptor_base[3] == 0xFB)
+				printf("Additional White Point Data\n");
+			
+			if(descriptor_base[3] == 0xFA)
+				printf("Additional Standard Timing IDs\n");
+		} else {
+			printf("Detailed Timing Descriptor\n");
+		
+			pixel_clock =
+				(descriptor_base[1] << 8) |
+				descriptor_base[0];
+			printf("Pixel Clock: %u * 10KHz\n", pixel_clock);
+		
+			horizontal_active_pixels =
+				(((descriptor_base[4] >> 4) & 0x0F) << 8) |
+				descriptor_base[2];
+			printf("Horzontal Active Pixels: %u\n",
+					horizontal_active_pixels);
+
+			horizontal_blanking_pixels =
+				((descriptor_base[4] & 0x0F) << 8) |
+				descriptor_base[3];
+			printf("Horizontal Blanking Pixels: %u\n",
+					horizontal_blanking_pixels);
+
+			vertical_active_lines =
+				(((descriptor_base[7] >> 4) & 0x0F) << 8) |
+				descriptor_base[5];
+			printf("Vertical Active Lines: %u\n",
+					vertical_active_lines);
+
+			vertical_blanking_lines =
+				((descriptor_base[7] & 0x0F) << 8) |
+				descriptor_base[6];
+			printf("Vertical Blanking Lines: %u\n",
+					vertical_blanking_lines);
+	
+			horizontal_sync_offset =
+				(((descriptor_base[11] >> 6) & 0x03) << 8) |
+				descriptor_base[8];
+			printf("Horizontal Sync Offset: %u\n",
+					horizontal_sync_offset);
+
+			horizontal_sync_width =
+				(((descriptor_base[11] >> 4) & 0x03) << 8) |
+				descriptor_base[9];
+			printf("Horizontal Sync Width: %u\n",
+					horizontal_sync_width);
+	
+			vertical_sync_offset =
+				(((descriptor_base[11] >> 2) & 0x03) << 4) |
+				((descriptor_base[10] >> 4) & 0x0F);
+			printf("Vertical Sync Offset: %u\n",
+					vertical_sync_offset);
+
+			vertical_sync_width = 
+				((descriptor_base[11] & 0x03) << 4) |
+				(descriptor_base[10] & 0x0F);
+			printf("Vertical Sync Width: %u\n",
+					vertical_sync_width);
+
+			horizontal_border_pixels = descriptor_base[15];
+			printf("Horizontal Border Pixels: %u\n",
+					horizontal_border_pixels);
+
+			vertical_border_pixels = descriptor_base[16];
+			printf("Vertical Border Pixels: %u\n",
+					vertical_border_pixels);
+
+			interlaced = (descriptor_base[17] & 0x80) ? 1 : 0;
+			printf("Interlaced: %u\n", interlaced);
+		}
+	}
+	
+	/* check for extension blocks */
+	printf("\nNumber of extension blocks: %u\n", adv7513_edid_buffer[126]);
+	if(adv7513_edid_buffer[126] == 0)
+		goto end_program;
+
+	/* verify extension block checksum */
+	checksum = 0;
+	for(i = 0 ; i < 128 ; i++)
+		checksum += adv7513_edid_buffer[128 + i];
+
+	if(checksum != 0) {
+		print_str = "extension block 1 checksum";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(2);
+	}
+
+	printf("\nEDID checksum block 1 is valid.\n");
+			
+	/* verify extension tag */
+	if(adv7513_edid_buffer[128] != 0x02) {
+		print_str = "extension tag";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(3);
+	}
+
+	printf("Extension Tag is valid.\n");
+
+	/* verify revision number */
+	if(adv7513_edid_buffer[129] != 0x03) {
+		print_str = "extension revision number";
+		printf("%s%s\n", ERROR_STR, print_str);
+		return(4);
+	}
+
+	printf("Extension revision is 3.\n");
+
+	/* checck for DTDs in extension block */
+	dtd_offset = adv7513_edid_buffer[130];