diff --git a/main.c b/edid.c similarity index 80% rename from main.c rename to edid.c index 171b0ce..11d1249 100644 --- a/main.c +++ b/edid.c @@ -1,11 +1,4 @@ -#include -#include -#include -#include -#include "frozen/frozen.h" - -#define EDID_LEN 128 -#define EDID_DISPLAY_DESCRIPTOR_DATA_LEN 13 +#include "edid.h" enum edid_analog_display_t { EDID_DISPLAY_TYPE_MONOCHROME_OR_GRAYSCALE = 0, @@ -27,217 +20,12 @@ enum edid_sync_mode_t { EDID_SYNC_MODE_DIGITAL_SEPERATE = 3 }; -typedef struct __attribute__ (( packed )) { - uint8_t x_resolution; - uint8_t vertical_frequency:6; - uint8_t aspect_ratio :2; -} standard_timing_information_t; - -typedef struct __attribute__ (( packed )) { - uint16_t pixel_clock; // 0-1 - uint8_t horizontal_active_pixels_lsbs; // 2 - uint8_t horizontal_blanking_pixels_lsbs; // 3 - uint8_t horizontal_blanking_pixels_msbs :4; // 4 - uint8_t horizontal_active_pixels_msbs :4; - uint8_t vertical_active_lines_lsbs; // 5 - uint8_t vertical_blanking_lines_lsbs; // 6 - uint8_t vertical_blanking_lines_msbs :4; // 7 - uint8_t vertical_active_lines_msbs :4; - uint8_t horizontal_front_porch_pixels_lsbs; // 8 - uint8_t horizontal_sync_pulse_pixels_lsbs; // 9 - uint8_t vertical_sync_pulse_lines_lsbs :4; // 10 - uint8_t vertical_front_porch_lines_lsbs :4; - uint8_t vertical_sync_pulse_lines_msbs :2; // 11 - uint8_t vertical_front_porch_lines_msbs :2; - uint8_t horizontal_sync_pulse_pixels_msbs :2; - uint8_t horizontal_front_porch_pixels_msbs :2; - uint8_t horizontal_image_size_lsbs; // 12 - uint8_t vertical_image_size_lsbs; - uint8_t vertical_image_size_msbs :4; - uint8_t horizontal_image_size_msbs :4; - uint8_t horizontal_border_pixels; - uint8_t vertical_border_lines; - // Feature bitmap - union __attribute__ (( packed )) { - struct __attribute__ (( packed )) { - uint8_t stereo_mode_lsb :1; - uint8_t sync_on_red_blue :1; - uint8_t vertical_sync_serration :1; - uint8_t sync_type :1; - uint8_t analog_sync :1; - uint8_t stereo_mode_msbs :2; - uint8_t interlaced :1; - } analog; - struct __attribute__ (( packed )) { - uint8_t stereo_mode_lsb :1; - uint8_t reserved :1; - uint8_t vertical_sync_polarity :1; - uint8_t digital_composite_sync :2; - uint8_t stereo_mode_msbs :2; - uint8_t interlaced :1; - } digital_composite; - struct __attribute__ (( packed )) { - uint8_t stereo_mode_lsb :1; - uint8_t horizontal_sync_polarity :1; - uint8_t vertical_sync_serration :1; - uint8_t digital_seperate_sync :2; - uint8_t stereo_mode_msbs :2; - uint8_t interlaced :1; - } digital_seperate; - } features; -} detailed_timing_descriptor_t; - -typedef struct __attribute__ (( packed )) { - uint16_t display_descriptor; - uint8_t reserved; - uint8_t display_range_limits_descriptor; - struct __attribute__ (( packed )) { - uint8_t vertical :2; - uint8_t horizontal :2; - uint8_t reserved :4; - } rate_offsets; - struct __attribute__ (( packed )) { - uint8_t minimum; - uint8_t maximum; - } vertical_field_rate; - struct __attribute__ (( packed )) { - uint8_t minimum; - uint8_t maximum; - } horizontal_line_rate; - uint8_t maximum_pixel_clock_mhz; // In 10 MHz units - uint8_t extended_timing_information_type; - uint8_t video_timing_params[7]; -} range_limits_descriptor_t; - -typedef struct __attribute__ (( packed )) { - uint16_t display_descriptor; - uint8_t reserved0; - uint8_t descriptor_type; - uint8_t reserved1; - uint8_t descriptor_data[EDID_DISPLAY_DESCRIPTOR_DATA_LEN]; -} display_descriptor_t; - -typedef struct __attribute__ (( packed )) { - /*! Header information, 20 bytes */ - struct __attribute__ (( packed )) { - uint64_t preamble; - uint8_t manufacturer_id[2]; - uint16_t product_code; - uint32_t serial_number; - uint8_t week_of_manufacture; - uint8_t year_of_manufacture; - uint8_t edid_version; - uint8_t edid_revision; - } header; - /*! Basic display parameters, 5 bytes */ - struct __attribute__ (( packed )) { - union __attribute__ (( packed )) { - struct __attribute__ (( packed )) { - uint8_t video_interface : 4; - uint8_t bit_depth : 3; - uint8_t input_type : 1; // 1 for digital - } digital; - struct __attribute__ (( packed )) { - uint8_t vsync_serrated: 1; - uint8_t sync_on_green : 1; - uint8_t composite_sync: 1; - uint8_t seperate_sync : 1; - uint8_t blank_to_black: 1; - uint8_t levels : 2; - uint8_t input_type : 1; // 0 for analog - } analog; - } video_input_parameters; // 1 byte - uint8_t horizontal_screen_size; - uint8_t vertical_screen_size; - uint8_t gamma; - struct __attribute__ (( packed )) { - uint8_t continuous_timings : 1; - uint8_t preferred_timing_mode : 1; - uint8_t standard_srgb : 1; - uint8_t display_type : 2; - uint8_t dpms_active_off_supported : 1; - uint8_t dpms_suspend_supported : 1; - uint8_t dpms_standby_supported : 1; - } features; - } basic_display_parameters; - /*! Chromaticity coordinates. 10-bit CIE 1931 xy coordinates for red, green, - * blue, and white point, 10 bytes */ - struct __attribute__ (( packed )) { - struct __attribute__ (( packed )) { - uint8_t green_y : 2; - uint8_t green_x : 2; - uint8_t red_y : 2; - uint8_t red_x : 2; - } red_green_lsbs; - struct __attribute__ (( packed )) { - uint8_t white_y : 2; - uint8_t white_x : 2; - uint8_t blue_y : 2; - uint8_t blue_x : 2; - } blue_white_lsbs; - uint8_t red_x_msbs; - uint8_t red_y_msbs; - uint8_t green_x_msbs; - uint8_t green_y_msbs; - uint8_t blue_x_msbs; - uint8_t blue_y_msbs; - uint8_t white_x_msbs; - uint8_t white_y_msbs; - } chromaticity_coordinates; - /*! Established timing bitmap. Supported bitmap for (formerly) very common - timing modes. 3 bytes */ - struct __attribute__ (( packed )) { - uint8_t m800_600_60 : 1; - uint8_t m800_600_56 : 1; - uint8_t m640_480_75 : 1; - uint8_t m640_480_72 : 1; - uint8_t m640_480_67 : 1; - uint8_t m640_480_60 : 1; - uint8_t m720_400_88 : 1; - uint8_t m720_400_70 : 1; - uint8_t m1280_1024_75: 1; - uint8_t m1024_768_75 : 1; - uint8_t m1024_768_70 : 1; - uint8_t m1024_768_60 : 1; - uint8_t m1024_768_87 : 1; - uint8_t m832_624_75 : 1; - uint8_t m800_600_75 : 1; - uint8_t m800_600_72 : 1; - uint8_t reserved : 7; - uint8_t m1152_870_75 : 1; - } common_timing_modes; - /*! Standard timing information. Up to 8 2-byte fields describing standard - display modes. Unused fields are filled with 01 01 hex. 16 bytes */ - standard_timing_information_t timings[8]; - detailed_timing_descriptor_t detailed_timing_descriptor; - range_limits_descriptor_t range_limits_descriptor; - display_descriptor_t display_name_descriptor; - display_descriptor_t display_serial_number_descriptor; - uint8_t number_extensions; - uint8_t checksum; -} edid_t; - static int stp_round(const float in) { return (int)(in < 0 ? (in - 0.5) : (in + 0.5)); } -/** - * @brief test function to show buffer - */ -static void disp_buf(uint8_t *buf, int len) -{ - int i; - for (i = 0; i < len; i++) { - printf("%02x ", buf[i]); - if ((i + 1) % 16 == 0) { - printf("\n"); - } - } - printf("\n"); -} - -static size_t file_to_str(unsigned char** str, const char const* filename) +static size_t file_to_str(char** str, const char const* filename) { size_t length; FILE * f = fopen (filename, "rb"); @@ -674,7 +462,6 @@ static int generate_timings(edid_t* edid, // X resolution json_scanf(multi_use_token.ptr, multi_use_token.len, "{x_resolution: %f}", &multi_use_flt); edid->timings[i].x_resolution = stp_round(multi_use_flt/8-31); - printf("res> %f\n", multi_use_flt); // Aspect ratio json_scanf(multi_use_token.ptr, multi_use_token.len, "{aspect_ratio: %Q}", &multi_use_str); if (strcmp(multi_use_str, "16:10") == 0) { @@ -896,7 +683,6 @@ static int generate_range_limit_descriptor( int n = json_scanf( json_str, json_len, "{range_limits_descriptor: {rate_offsets: {vertical: %Q}}}", &multi_use_str); - printf("n>%d\n", n); if (strcmp(multi_use_str, "none") == 0) { edid->range_limits_descriptor.rate_offsets.vertical = 0; } else { @@ -1057,29 +843,66 @@ static int generate_checksum( return 0; } -int main(void) +int generate_edid(edid_t* const edid, const char* const filename) { - edid_t edith; - memset(&edith, 0x00, sizeof(edith)); - printf("size> %d bytes\n", sizeof(edith)); - generate_preamble(&edith); - unsigned char* mystr = NULL; - size_t len = file_to_str(&mystr, "edid_stockach.json"); - if (mystr) { - printf("%s\n", mystr); - printf("len> %d\n", len); + char* json_str = NULL; + size_t len = 0; + + if (edid && filename) { + // Initialize with all 0x00 + memset(edid, 0x00, EDID_LEN); + len = file_to_str(&json_str, filename); + if (json_str) { + generate_preamble(edid); + if (generate_header(edid, json_str, len)) { + printf("Error: Problem generating header.\n"); + return 1; + } + if (generate_basic_display_parameters(edid, json_str, len)) { + printf("Error: Problem generating basic display parameters.\n"); + return 1; + } + if (generate_chromaticity_coordinates(edid, json_str, len)) { + printf("Error: Problem generating cromaticity coordinates.\n"); + return 1; + } + if (generate_common_timing_modes(edid, json_str, len)) { + printf("Error: Problem generating commong timing modes.\n"); + return 1; + } + if (generate_timings(edid, json_str, len)) { + printf("Error: Problem generating timings.\n"); + return 1; + } + if (generate_detailed_timing_descriptor(edid, json_str, len)) { + printf("Error: Problem generating timing descriptor.\n"); + return 1; + } + if (generate_range_limit_descriptor(edid, json_str, len)) { + printf("Error: Problem generating range limit descriptor.\n"); + return 1; + } + if (generate_display_name_descriptor(edid, json_str, len)) { + printf("Error: Problem generating display name descriptor.\n"); + return 1; + } + if (generate_display_serial_number_descriptor(edid, json_str, len)) { + printf("Error: Problem generating serial number descriptor.\n"); + return 1; + } + if (generate_checksum(edid, json_str, len)) { + printf("Error: Problem generating checksum.\n"); + return 1; + } + // We do not need the JSON string anymore + free(json_str); + } else { + printf("Error: json_str is NULL"); + return 1; + } + } else { + printf("Error: One of either edid or filename is NULL.\n"); + return 1; } - generate_header(&edith, mystr, len); - generate_basic_display_parameters(&edith, mystr, len); - generate_chromaticity_coordinates(&edith, mystr, len); - generate_common_timing_modes(&edith, mystr, len); - generate_timings(&edith, mystr, len); - generate_detailed_timing_descriptor(&edith, mystr, len); - generate_range_limit_descriptor(&edith, mystr, len); - generate_display_name_descriptor(&edith, mystr, len); - generate_display_serial_number_descriptor(&edith, mystr, len); - generate_checksum(&edith, mystr, len); - disp_buf((uint8_t*)&edith, EDID_LEN); - free(mystr); return 0; } diff --git a/edid.h b/edid.h new file mode 100644 index 0000000..d31415e --- /dev/null +++ b/edid.h @@ -0,0 +1,208 @@ +#ifndef INCLUDED_MAX_EDID +#define INCLUDED_MAX_EDID + +#include +#include +#include +#include +#include "frozen/frozen.h" + +#define EDID_LEN 128 +#define EDID_DISPLAY_DESCRIPTOR_DATA_LEN 13 + +// EDID data structure + +typedef struct __attribute__ (( packed )) { + uint8_t x_resolution; + uint8_t vertical_frequency:6; + uint8_t aspect_ratio :2; +} standard_timing_information_t; + +typedef struct __attribute__ (( packed )) { + uint16_t pixel_clock; // 0-1 + uint8_t horizontal_active_pixels_lsbs; // 2 + uint8_t horizontal_blanking_pixels_lsbs; // 3 + uint8_t horizontal_blanking_pixels_msbs :4; // 4 + uint8_t horizontal_active_pixels_msbs :4; + uint8_t vertical_active_lines_lsbs; // 5 + uint8_t vertical_blanking_lines_lsbs; // 6 + uint8_t vertical_blanking_lines_msbs :4; // 7 + uint8_t vertical_active_lines_msbs :4; + uint8_t horizontal_front_porch_pixels_lsbs; // 8 + uint8_t horizontal_sync_pulse_pixels_lsbs; // 9 + uint8_t vertical_sync_pulse_lines_lsbs :4; // 10 + uint8_t vertical_front_porch_lines_lsbs :4; + uint8_t vertical_sync_pulse_lines_msbs :2; // 11 + uint8_t vertical_front_porch_lines_msbs :2; + uint8_t horizontal_sync_pulse_pixels_msbs :2; + uint8_t horizontal_front_porch_pixels_msbs :2; + uint8_t horizontal_image_size_lsbs; // 12 + uint8_t vertical_image_size_lsbs; + uint8_t vertical_image_size_msbs :4; + uint8_t horizontal_image_size_msbs :4; + uint8_t horizontal_border_pixels; + uint8_t vertical_border_lines; + // Feature bitmap + union __attribute__ (( packed )) { + struct __attribute__ (( packed )) { + uint8_t stereo_mode_lsb :1; + uint8_t sync_on_red_blue :1; + uint8_t vertical_sync_serration :1; + uint8_t sync_type :1; + uint8_t analog_sync :1; + uint8_t stereo_mode_msbs :2; + uint8_t interlaced :1; + } analog; + struct __attribute__ (( packed )) { + uint8_t stereo_mode_lsb :1; + uint8_t reserved :1; + uint8_t vertical_sync_polarity :1; + uint8_t digital_composite_sync :2; + uint8_t stereo_mode_msbs :2; + uint8_t interlaced :1; + } digital_composite; + struct __attribute__ (( packed )) { + uint8_t stereo_mode_lsb :1; + uint8_t horizontal_sync_polarity :1; + uint8_t vertical_sync_serration :1; + uint8_t digital_seperate_sync :2; + uint8_t stereo_mode_msbs :2; + uint8_t interlaced :1; + } digital_seperate; + } features; +} detailed_timing_descriptor_t; + +typedef struct __attribute__ (( packed )) { + uint16_t display_descriptor; + uint8_t reserved; + uint8_t display_range_limits_descriptor; + struct __attribute__ (( packed )) { + uint8_t vertical :2; + uint8_t horizontal :2; + uint8_t reserved :4; + } rate_offsets; + struct __attribute__ (( packed )) { + uint8_t minimum; + uint8_t maximum; + } vertical_field_rate; + struct __attribute__ (( packed )) { + uint8_t minimum; + uint8_t maximum; + } horizontal_line_rate; + uint8_t maximum_pixel_clock_mhz; // In 10 MHz units + uint8_t extended_timing_information_type; + uint8_t video_timing_params[7]; +} range_limits_descriptor_t; + +typedef struct __attribute__ (( packed )) { + uint16_t display_descriptor; + uint8_t reserved0; + uint8_t descriptor_type; + uint8_t reserved1; + uint8_t descriptor_data[EDID_DISPLAY_DESCRIPTOR_DATA_LEN]; +} display_descriptor_t; + +typedef struct __attribute__ (( packed )) { + /*! Header information, 20 bytes */ + struct __attribute__ (( packed )) { + uint64_t preamble; + uint8_t manufacturer_id[2]; + uint16_t product_code; + uint32_t serial_number; + uint8_t week_of_manufacture; + uint8_t year_of_manufacture; + uint8_t edid_version; + uint8_t edid_revision; + } header; + /*! Basic display parameters, 5 bytes */ + struct __attribute__ (( packed )) { + union __attribute__ (( packed )) { + struct __attribute__ (( packed )) { + uint8_t video_interface : 4; + uint8_t bit_depth : 3; + uint8_t input_type : 1; // 1 for digital + } digital; + struct __attribute__ (( packed )) { + uint8_t vsync_serrated: 1; + uint8_t sync_on_green : 1; + uint8_t composite_sync: 1; + uint8_t seperate_sync : 1; + uint8_t blank_to_black: 1; + uint8_t levels : 2; + uint8_t input_type : 1; // 0 for analog + } analog; + } video_input_parameters; // 1 byte + uint8_t horizontal_screen_size; + uint8_t vertical_screen_size; + uint8_t gamma; + struct __attribute__ (( packed )) { + uint8_t continuous_timings : 1; + uint8_t preferred_timing_mode : 1; + uint8_t standard_srgb : 1; + uint8_t display_type : 2; + uint8_t dpms_active_off_supported : 1; + uint8_t dpms_suspend_supported : 1; + uint8_t dpms_standby_supported : 1; + } features; + } basic_display_parameters; + /*! Chromaticity coordinates. 10-bit CIE 1931 xy coordinates for red, green, + * blue, and white point, 10 bytes */ + struct __attribute__ (( packed )) { + struct __attribute__ (( packed )) { + uint8_t green_y : 2; + uint8_t green_x : 2; + uint8_t red_y : 2; + uint8_t red_x : 2; + } red_green_lsbs; + struct __attribute__ (( packed )) { + uint8_t white_y : 2; + uint8_t white_x : 2; + uint8_t blue_y : 2; + uint8_t blue_x : 2; + } blue_white_lsbs; + uint8_t red_x_msbs; + uint8_t red_y_msbs; + uint8_t green_x_msbs; + uint8_t green_y_msbs; + uint8_t blue_x_msbs; + uint8_t blue_y_msbs; + uint8_t white_x_msbs; + uint8_t white_y_msbs; + } chromaticity_coordinates; + /*! Established timing bitmap. Supported bitmap for (formerly) very common + timing modes. 3 bytes */ + struct __attribute__ (( packed )) { + uint8_t m800_600_60 : 1; + uint8_t m800_600_56 : 1; + uint8_t m640_480_75 : 1; + uint8_t m640_480_72 : 1; + uint8_t m640_480_67 : 1; + uint8_t m640_480_60 : 1; + uint8_t m720_400_88 : 1; + uint8_t m720_400_70 : 1; + uint8_t m1280_1024_75: 1; + uint8_t m1024_768_75 : 1; + uint8_t m1024_768_70 : 1; + uint8_t m1024_768_60 : 1; + uint8_t m1024_768_87 : 1; + uint8_t m832_624_75 : 1; + uint8_t m800_600_75 : 1; + uint8_t m800_600_72 : 1; + uint8_t reserved : 7; + uint8_t m1152_870_75 : 1; + } common_timing_modes; + /*! Standard timing information. Up to 8 2-byte fields describing standard + display modes. Unused fields are filled with 01 01 hex. 16 bytes */ + standard_timing_information_t timings[8]; + detailed_timing_descriptor_t detailed_timing_descriptor; + range_limits_descriptor_t range_limits_descriptor; + display_descriptor_t display_name_descriptor; + display_descriptor_t display_serial_number_descriptor; + uint8_t number_extensions; + uint8_t checksum; +} edid_t; + +// Interface +int generate_edid(edid_t* const edid, const char* const filename); + +#endif diff --git a/edid_stockach.json b/edid0.json similarity index 100% rename from edid_stockach.json rename to edid0.json diff --git a/edid_wiki.pdf b/edid_wiki.pdf new file mode 100644 index 0000000..7ee83ce Binary files /dev/null and b/edid_wiki.pdf differ diff --git a/unit_test.c b/unit_test.c new file mode 100644 index 0000000..ab7c153 --- /dev/null +++ b/unit_test.c @@ -0,0 +1,78 @@ +#include +#include "edid.h" + +#define FAILURE(buf_str, line) \ + fprintf(stderr, "X Failed on line %d: [%s]\n", line, buf_str); \ + return EXIT_FAILURE; + +#define SUCCESS(buf_str, line) \ + fprintf(stdout, "✓ Success on line %d: [%s]\n", line, buf_str); + +#define CHECK_TRUE(expr) \ + if (!(expr)) { \ + FAILURE(#expr, __LINE__); \ + } else { \ + SUCCESS(#expr, __LINE__); \ + } + +static const uint8_t i2c_data_screen_0[EDID_LEN] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1e, 0x6d, 0xd5, 0x59, 0x04, 0x45, 0x09, 0x00, + 0x03, 0x17, 0x01, 0x03, 0x68, 0x35, 0x1e, 0x78, 0xea, 0x33, 0x31, 0xa4, 0x57, 0x51, 0xa0, 0x26, + 0x10, 0x50, 0x54, 0xa7, 0x6b, 0x80, 0xb3, 0x00, 0x81, 0x80, 0x95, 0x00, 0x71, 0x4f, 0xa9, 0xc0, + 0x81, 0x00, 0x81, 0xc0, 0x90, 0x40, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4b, 0x1e, + 0x53, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x32, + 0x34, 0x45, 0x4e, 0x33, 0x33, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x33, 0x30, 0x33, 0x4e, 0x44, 0x51, 0x41, 0x48, 0x56, 0x34, 0x39, 0x32, 0x0a, 0x00, 0xf1 +}; + +/** + * @brief Helper function to compare EDID data structure with golden design data structure extracted from real + * VGA screen. + */ +static int compare_edid(const uint8_t* const edid, const uint8_t* const edid_golden) +{ + for (int sanny=0; sanny %d\n", ret); + return ret; + } + printf("\n"); + printf("Generated EDID data from %s:\n", json_file); + disp_buf((uint8_t*)&edith, EDID_LEN); + return 0; +}