|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "frozen/frozen.h"
|
|
|
|
|
|
|
|
#define EDID_LEN 128
|
|
|
|
#define EDID_DISPLAY_DESCRIPTOR_DATA_LEN 13
|
|
|
|
|
|
|
|
enum edid_analog_display_t {
|
|
|
|
EDID_DISPLAY_TYPE_MONOCHROME_OR_GRAYSCALE = 0,
|
|
|
|
EDID_DISPLAY_TYPE_RGB_COLOR = 1,
|
|
|
|
EDID_DISPLAY_TYPE_NON_RGB_COLOR = 2,
|
|
|
|
EDID_DISPLAY_TYPE_UNDEFINED = 3
|
|
|
|
};
|
|
|
|
|
|
|
|
enum edid_aspect_ratio_t {
|
|
|
|
EDID_ASPECT_RATIO_16_10 = 0,
|
|
|
|
EDID_ASPECT_RATIO_4_3 = 1,
|
|
|
|
EDID_ASPECT_RATIO_5_4 = 2,
|
|
|
|
EDID_ASPECT_RATIO_16_9 = 3
|
|
|
|
};
|
|
|
|
|
|
|
|
enum edid_sync_mode_t {
|
|
|
|
EDID_SYNC_MODE_ANALOG = 0,
|
|
|
|
EDID_SYNC_MODE_DIGITAL_COMPOSITE = 2,
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
size_t length;
|
|
|
|
FILE * f = fopen (filename, "rb");
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
fseek (f, 0, SEEK_END);
|
|
|
|
length = ftell (f);
|
|
|
|
fseek (f, 0, SEEK_SET);
|
|
|
|
*str = (unsigned char*)malloc(length);
|
|
|
|
if (*str) {
|
|
|
|
fread (*str, 1, length, f);
|
|
|
|
}
|
|
|
|
fclose (f);
|
|
|
|
} else {
|
|
|
|
printf("File pointer is NULL..\n");
|
|
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void generate_preamble(edid_t* edid)
|
|
|
|
{
|
|
|
|
memset((uint8_t*)&(edid->header.preamble)+0, 0x00, 1);
|
|
|
|
memset((uint8_t*)&(edid->header.preamble)+1, 0xFF, 6);
|
|
|
|
memset((uint8_t*)&(edid->header.preamble)+7, 0x00, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_header(edid_t* edid, unsigned char* json_str, size_t json_len)
|
|
|
|
{
|
|
|
|
char* multi_use_str;
|
|
|
|
unsigned int multi_use_int;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
// Manufacturer ID
|
|
|
|
json_scanf( json_str, json_len, "{header: {manufacturer_id: %Q}",
|
|
|
|
&multi_use_str);
|
|
|
|
if (strlen(multi_use_str) == 3) {
|
|
|
|
multi_use_int = 0;
|
|
|
|
multi_use_int |=
|
|
|
|
((0x1F & (multi_use_str[0]-64)) << 10)| // First letter
|
|
|
|
((0x1F & (multi_use_str[1]-64)) << 5) | // Second letter
|
|
|
|
((0x1F & (multi_use_str[2]-64)) << 0); // Third letter
|
|
|
|
edid->header.manufacturer_id[0] = multi_use_int >> 8;
|
|
|
|
edid->header.manufacturer_id[1] = multi_use_int;
|
|
|
|
} else {
|
|
|
|
printf("Error: JSON file format is wrong somewhere arround manufacturer\
|
|
|
|
ID.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// %Q mallocs a string
|
|
|
|
free(multi_use_str);
|
|
|
|
// Product code
|
|
|
|
json_scanf(json_str, json_len, "{header: {product_code: %d}", &multi_use_int);
|
|
|
|
edid->header.product_code = 0xFFFF & multi_use_int;
|
|
|
|
// Serial number
|
|
|
|
json_scanf(json_str, json_len, "{header: {serial_number: %d}", &multi_use_int);
|
|
|
|
edid->header.serial_number = 0xFFFFFFFF & multi_use_int;
|
|
|
|
// Week of manufacture
|
|
|
|
json_scanf( json_str, json_len, "{header: {week_of_manufacture: %d}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->header.week_of_manufacture = 0xFF & multi_use_int;
|
|
|
|
// Year of manufacture
|
|
|
|
json_scanf( json_str, json_len, "{header: {year_of_manufacture: %d}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->header.year_of_manufacture = 0xFF & (multi_use_int - 1990);
|
|
|
|
// EDID version
|
|
|
|
json_scanf(json_str, json_len, "{header: {edid_version: %d}", &multi_use_int);
|
|
|
|
edid->header.edid_version = 0xFF & multi_use_int;
|
|
|
|
// EDID revision
|
|
|
|
json_scanf(json_str, json_len, "{header: {edid_revision: %d}", &multi_use_int);
|
|
|
|
edid->header.edid_revision = 0xFF & multi_use_int;
|
|
|
|
} else {
|
|
|
|
printf("One of either edid or json_str is NULL.\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_basic_display_parameters( edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
char* multi_use_str;
|
|
|
|
float multi_use_flt0;
|
|
|
|
float multi_use_flt1;
|
|
|
|
unsigned int multi_use_int;
|
|
|
|
bool multi_use_bool;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
// Input type analog or digital
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{input_type: %Q}}}",
|
|
|
|
&multi_use_str);
|
|
|
|
if (strcmp(multi_use_str, "analog") == 0) {
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.input_type = 0;
|
|
|
|
// Levels
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{levels: {upper: %f}}}",
|
|
|
|
&multi_use_flt0);
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{levels: {lower: %f}}}",
|
|
|
|
&multi_use_flt1);
|
|
|
|
if (multi_use_flt0 == 0.7f && multi_use_flt1 == -0.3f) {
|
|
|
|
multi_use_int = 0;
|
|
|
|
} else if (multi_use_flt0 == 0.714f && multi_use_flt1 == -0.286f) {
|
|
|
|
multi_use_int = 1;
|
|
|
|
} else if (multi_use_flt0 == 1.0f && multi_use_flt1 == -0.4f) {
|
|
|
|
multi_use_int = 2;
|
|
|
|
} else if (multi_use_flt0 == 0.7f && multi_use_flt1 == 0.0f) {
|
|
|
|
multi_use_int = 3;
|
|
|
|
} else {
|
|
|
|
printf("Error: The levels %f/%f are not conforming to the standard.\n",
|
|
|
|
multi_use_flt0, multi_use_flt1);
|
|
|
|
}
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.levels = 0x3 & multi_use_int;
|
|
|
|
// Blank to black
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{blank_to_black: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.blank_to_black = 0x1 & multi_use_bool;
|
|
|
|
// Seperate sync
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{seperate_sync: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.seperate_sync = 0x1 & multi_use_bool;
|
|
|
|
// Composite sync
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{composite_sync: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.composite_sync = 0x1 & multi_use_bool;
|
|
|
|
// Sync on green
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {video_input_parameters: \
|
|
|
|
{sync_on_green: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.sync_on_green = 0x1 & multi_use_bool;
|
|
|
|
// Vsync serrated
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{video_input_parameters: {sync_on_green: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters.video_input_parameters
|
|
|
|
.analog.blank_to_black = 0x1 & multi_use_bool;
|
|
|
|
} else if (strcmp(multi_use_str, "digital") == 0) {
|
|
|
|
printf("Digital video configuration is not yet supported.\n");
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
printf("Error: JSON file format is wrong somewhere around\
|
|
|
|
the video input params.\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
// %Q mallocs a string
|
|
|
|
free(multi_use_str);
|
|
|
|
// Horizontal screen size
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {horizontal_screen_size: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.horizontal_screen_size = multi_use_int;
|
|
|
|
// Vertical screen size
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {vertical_screen_size: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.vertical_screen_size = multi_use_int;
|
|
|
|
// Horizontal screen size
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {gamma: %f}}",
|
|
|
|
&multi_use_flt0);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.gamma = (multi_use_flt0-1)*100;
|
|
|
|
// Display Power Management Signaling (DPMS)
|
|
|
|
// Standby
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: {dpms: \
|
|
|
|
{standby_supported: %B}}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.dpms_standby_supported = 0x1 & multi_use_bool;
|
|
|
|
// Suspend
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: {dpms: \
|
|
|
|
{suspend_supported: %B}}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.dpms_suspend_supported = 0x1 & multi_use_bool;
|
|
|
|
// Active-off
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: {dpms: \
|
|
|
|
{active_off_supported: %B}}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.dpms_active_off_supported = 0x1 & multi_use_bool;
|
|
|
|
// Display type
|
|
|
|
char a_str[] = "analog";
|
|
|
|
char d_str[] = "digital";
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: {display_type: %Q}}}",
|
|
|
|
&multi_use_str);
|
|
|
|
// Create substrings for comparison
|
|
|
|
memcpy(a_str, multi_use_str, strlen(a_str));
|
|
|
|
memcpy(d_str, multi_use_str, strlen(d_str));
|
|
|
|
if (strcmp(a_str, "analog") == 0) {
|
|
|
|
// Remove "analog_"
|
|
|
|
memcpy( multi_use_str, multi_use_str+strlen(a_str)+1,
|
|
|
|
strlen(multi_use_str)-strlen(a_str));
|
|
|
|
if (strcmp(multi_use_str, "monochrome_or_grayscale") == 0) {
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.display_type = 0x3 & EDID_DISPLAY_TYPE_MONOCHROME_OR_GRAYSCALE;
|
|
|
|
} else if (strcmp(multi_use_str, "rgb_color") == 0) {
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.display_type = 0x3 & ~EDID_DISPLAY_TYPE_RGB_COLOR;
|
|
|
|
} else if (strcmp(multi_use_str, "non_rgb_color") == 0) {
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.display_type = 0x3 & ~EDID_DISPLAY_TYPE_NON_RGB_COLOR;
|
|
|
|
} else if (strcmp(multi_use_str, "undefined") == 0) {
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.display_type = 0x3 & EDID_DISPLAY_TYPE_UNDEFINED;
|
|
|
|
} else {
|
|
|
|
printf("Error: Unknown analog display_type.\n");
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
} else if (strcmp(d_str, "digital") == 0) {
|
|
|
|
printf("Error: Digital format is not supported yet.\n");
|
|
|
|
return 4;
|
|
|
|
} else {
|
|
|
|
printf("Error: JSON format is wrong. First word needs of display_type \
|
|
|
|
should either be analog or digital.\n");
|
|
|
|
}
|
|
|
|
// %Q mallocs a string, which needs to be freed
|
|
|
|
free(multi_use_str);
|
|
|
|
// Standard sRGB
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: {standard_srgb: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.standard_srgb = 0x1 & multi_use_bool;
|
|
|
|
// Prefered timing mode
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: \
|
|
|
|
{preferred_timing_mode: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.preferred_timing_mode = 0x1 & multi_use_bool;
|
|
|
|
// Continuous timings
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{basic_display_parameters: {features: {continuous_timings: %B}}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->basic_display_parameters
|
|
|
|
.features.continuous_timings = 0x1 & multi_use_bool;
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_chromaticity_coordinates( edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
float multi_use_flt;
|
|
|
|
unsigned int multi_use_int;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
// Red x
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {red_x: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.red_green_lsbs.red_x = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.red_x_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// Red y
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {red_y: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.red_green_lsbs.red_y = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.red_y_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// Green x
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {green_x: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.red_green_lsbs.green_x = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.green_x_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// Green y
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {green_y: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.red_green_lsbs.green_y = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.green_y_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// Blue x
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {blue_x: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.blue_white_lsbs.blue_x = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.blue_x_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// Blue y
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {blue_y: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.blue_white_lsbs.blue_y = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.blue_y_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// White x
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {white_x: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.blue_white_lsbs.white_x = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.white_x_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
// White y
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{chromaticity_coordinates: {white_y: %f}}",
|
|
|
|
&multi_use_flt);
|
|
|
|
multi_use_int = stp_round(1024.0f * multi_use_flt);
|
|
|
|
edid->chromaticity_coordinates.blue_white_lsbs.white_y = 0x3 & multi_use_int;
|
|
|
|
edid->chromaticity_coordinates.white_y_msbs = 0xFF & multi_use_int >> 2;
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_common_timing_modes( edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
bool multi_use_bool;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m720_400_70: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m720_400_70 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m720_400_88: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m720_400_88 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m640_480_60: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m640_480_60 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m640_480_67: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m640_480_67 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m640_480_72: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m640_480_72 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m640_480_75: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m640_480_75 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m800_600_56: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m800_600_56 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m800_600_60: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m800_600_60 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m800_600_72: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m800_600_72 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m800_600_75: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m800_600_75 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m832_624_75: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m832_624_75 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m1024_768_87: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m1024_768_87 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m1024_768_60: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m1024_768_60 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m1024_768_70: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m1024_768_70 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m1024_768_75: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m1024_768_75 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m1280_1024_75: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m1280_1024_75 = 0x1 & multi_use_bool;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{common_timing_modes: {m1152_870_75: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->common_timing_modes.m1152_870_75 = 0x1 & multi_use_bool;
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_timings(edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
struct json_token multi_use_token;
|
|
|
|
float multi_use_flt;
|
|
|
|
int multi_use_int;
|
|
|
|
char* multi_use_str;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
int i = 0;
|
|
|
|
for (i = 0; json_scanf_array_elem(json_str, json_len, ".timings", i, &multi_use_token) > 0; i++) {
|
|
|
|
// t.type == JSON_TYPE_OBJECT
|
|
|
|
// 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) {
|
|
|
|
edid->timings[i].aspect_ratio = 0x3 & EDID_ASPECT_RATIO_16_10;
|
|
|
|
} else if (strcmp(multi_use_str, "4:3") == 0) {
|
|
|
|
edid->timings[i].aspect_ratio = 0x3 & EDID_ASPECT_RATIO_4_3;
|
|
|
|
} else if (strcmp(multi_use_str, "5:4") == 0) {
|
|
|
|
edid->timings[i].aspect_ratio = 0x3 & EDID_ASPECT_RATIO_5_4;
|
|
|
|
} else if (strcmp(multi_use_str, "16:9") == 0) {
|
|
|
|
edid->timings[i].aspect_ratio = 0x3 & EDID_ASPECT_RATIO_16_9;
|
|
|
|
} else {
|
|
|
|
printf("Error: Aspect ratio not supported");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// Free string that has been allocated in memory
|
|
|
|
free(multi_use_str);
|
|
|
|
// Vertical frequency
|
|
|
|
json_scanf(multi_use_token.ptr, multi_use_token.len, "{vertical_frequency: %d}", &multi_use_int);
|
|
|
|
if (multi_use_int >= 60) {
|
|
|
|
edid->timings[i].vertical_frequency = multi_use_int-60;
|
|
|
|
} else {
|
|
|
|
printf("Error: Vertical frequency cannot be lower than 60 Hz.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_detailed_timing_descriptor(
|
|
|
|
edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
unsigned int multi_use_int;
|
|
|
|
float multi_use_float;
|
|
|
|
struct json_token multi_use_token;
|
|
|
|
bool multi_use_bool;
|
|
|
|
char* multi_use_str;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
// Pixel clock
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {pixel_clock_mhz: %f}}",
|
|
|
|
&multi_use_float);
|
|
|
|
/* The data is stored as 10 kHz unitsi in the EDID structure.
|
|
|
|
It is a float representing MHz in the JSON file.
|
|
|
|
*/
|
|
|
|
edid->detailed_timing_descriptor.pixel_clock = stp_round(multi_use_float/0.01f);
|
|
|
|
// Horizontal active pixels
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {horizontal_active_pixels: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_active_pixels_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_active_pixels_msbs = 0x0F & (multi_use_int >> 8);
|
|
|
|
// Horizontal blanking pixels
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {horizontal_blanking_pixels: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_blanking_pixels_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_blanking_pixels_msbs = 0x0F & (multi_use_int >> 8);
|
|
|
|
// Vertical active lines
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {vertical_active_lines: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_active_lines_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_active_lines_msbs = 0x0F & (multi_use_int >> 8);
|
|
|
|
// Vertical blanking lines
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {vertical_blanking_lines: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_blanking_lines_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_blanking_lines_msbs = 0x0F & (multi_use_int >> 8);
|
|
|
|
// Horizontal front porch pixels
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {horizontal_front_porch_pixels: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_front_porch_pixels_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_front_porch_pixels_msbs = 0x03 & (multi_use_int >> 8);
|
|
|
|
// Horizontal sync pulse pixels
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {horizontal_sync_pulse_pixels: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_sync_pulse_pixels_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_sync_pulse_pixels_msbs = 0x03 & (multi_use_int >> 8);
|
|
|
|
// Vertical front porch lines
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {vertical_front_porch_lines: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_front_porch_lines_lsbs = 0x0F & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_front_porch_lines_msbs = 0x03 & (multi_use_int >> 4);
|
|
|
|
// Vertical sync pulse lines
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {vertical_sync_pulse_lines: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_sync_pulse_lines_lsbs = 0x0F & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_sync_pulse_lines_msbs = 0x03 & (multi_use_int >> 4);
|
|
|
|
// Horizontal image size
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {horizontal_image_size_mm: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_image_size_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_image_size_msbs = 0x0F & (multi_use_int >> 8);
|
|
|
|
// Vertical image size
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {vertical_image_size_mm: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_image_size_lsbs = 0xFF & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_image_size_msbs = 0x0F & (multi_use_int >> 8);
|
|
|
|
// Horizontal border pixels
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {horizontal_border_pixels: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.horizontal_border_pixels = 0xFF & multi_use_int;
|
|
|
|
// Vertical border lines
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {vertical_border_lines: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.vertical_border_lines = 0xFF & multi_use_int;
|
|
|
|
// Sync features bitmap
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {stereo_mode: %Q}}",
|
|
|
|
&multi_use_str);
|
|
|
|
if (strcmp(multi_use_str, "none") == 0) {
|
|
|
|
multi_use_int = 0;
|
|
|
|
} else {
|
|
|
|
printf("Error: This stereo mode is not implemented yet.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
free(multi_use_str);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.features.digital_seperate.stereo_mode_lsb = 0x1 & multi_use_int;
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.features.digital_seperate.stereo_mode_msbs = 0x3 & (multi_use_int >> 1);
|
|
|
|
// Interlaced
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: {interlaced: %B}}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.features.digital_seperate.interlaced = multi_use_bool;
|
|
|
|
// Sync details
|
|
|
|
if (json_scanf(json_str, json_len,
|
|
|
|
"{detailed_timing_descriptor: { digital_seperate_sync: %T}}",
|
|
|
|
&multi_use_token) > 0) {
|
|
|
|
// Set sync mode
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.features.digital_seperate.digital_seperate_sync = 0x3 & EDID_SYNC_MODE_DIGITAL_SEPERATE;
|
|
|
|
// Vertical sync serration
|
|
|
|
json_scanf( multi_use_token.ptr, multi_use_token.len,
|
|
|
|
"{vertical_sync_serration: %B}",
|
|
|
|
&multi_use_bool);
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.features.digital_seperate.vertical_sync_serration = multi_use_bool;
|
|
|
|
// Horizontal sync polarity
|
|
|
|
json_scanf( multi_use_token.ptr, multi_use_token.len,
|
|
|
|
"{horizontal_sync_polarity: %Q}",
|
|
|
|
&multi_use_str);
|
|
|
|
if (strcmp(multi_use_str, "positive") == 0) {
|
|
|
|
multi_use_bool = true;
|
|
|
|
} else if (strcmp(multi_use_str, "negative") == 0) {
|
|
|
|
multi_use_bool = false;
|
|
|
|
} else {
|
|
|
|
printf("Error: The horizontal sync polarity can either be positive or negative (JSON string).\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
edid->detailed_timing_descriptor
|
|
|
|
.features.digital_seperate.horizontal_sync_polarity = multi_use_bool;
|
|
|
|
free(multi_use_str);
|
|
|
|
} else {
|
|
|
|
printf("Error: Could not parse JSON file somewhere arround sync \
|
|
|
|
features in detailed timing descriptor.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_range_limit_descriptor(
|
|
|
|
edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
char* multi_use_str;
|
|
|
|
unsigned multi_use_int;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
edid->range_limits_descriptor.display_descriptor = 0;
|
|
|
|
edid->range_limits_descriptor.reserved = 0;
|
|
|
|
edid->range_limits_descriptor.display_range_limits_descriptor = 0xFD;
|
|
|
|
// Range offsets
|
|
|
|
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 {
|
|
|
|
printf("Error: Range limit offset other than none is not yet implemented.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
free(multi_use_str);
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {rate_offsets: {horizontal: %Q}}}",
|
|
|
|
&multi_use_str);
|
|
|
|
if (strcmp(multi_use_str, "none") == 0) {
|
|
|
|
edid->range_limits_descriptor.rate_offsets.horizontal = 0;
|
|
|
|
} else {
|
|
|
|
printf("Error: Range limit offset other than none is not yet implemented.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
free(multi_use_str);
|
|
|
|
// Vertical and horizontal lines
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {rates: {vertical: {minimum_hz: %d}}}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->range_limits_descriptor.vertical_field_rate.minimum = 0xFF & multi_use_int;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {rates: {vertical: {maximum_hz: %d}}}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->range_limits_descriptor.vertical_field_rate.maximum = 0xFF & multi_use_int;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {rates: {horizontal: {minimum_khz: %d}}}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->range_limits_descriptor.horizontal_line_rate.minimum = 0xFF & multi_use_int;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {rates: {horizontal: {maximum_khz: %d}}}}",
|
|
|
|
&multi_use_int);
|
|
|
|
edid->range_limits_descriptor.horizontal_line_rate.maximum = 0xFF & multi_use_int;
|
|
|
|
// Maximum pixel clock rate
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {maximum_pixel_clock_mhz: %d}}",
|
|
|
|
&multi_use_int);
|
|
|
|
// Multiples of 10 MHz
|
|
|
|
edid->range_limits_descriptor.maximum_pixel_clock_mhz = 0xFF & (multi_use_int/10);
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{range_limits_descriptor: {extended_timing_information_type: %Q}}",
|
|
|
|
&multi_use_str);
|
|
|
|
if (strcmp(multi_use_str, "default_gtf") == 0) {
|
|
|
|
edid->range_limits_descriptor.extended_timing_information_type = 0;
|
|
|
|
// Apply padding for video timing parameters
|
|
|
|
edid->range_limits_descriptor.video_timing_params[0] = 0x0A;
|
|
|
|
memset(edid->range_limits_descriptor.video_timing_params + 1, 0x20, 6);
|
|
|
|
} else {
|
|
|
|
printf("Error: Any other timing information type than default_gtf is currently not implemented.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
free(multi_use_str);
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_display_name_descriptor(
|
|
|
|
edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
char* display_name = NULL;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
edid->display_name_descriptor.display_descriptor = 0;
|
|
|
|
edid->display_name_descriptor.reserved0 = 0;
|
|
|
|
edid->display_name_descriptor.descriptor_type = 0xFC;
|
|
|
|
edid->display_name_descriptor.reserved1 = 0;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{display_name_descriptor: {name: %Q}}",
|
|
|
|
&display_name);
|
|
|
|
if (display_name != NULL) {
|
|
|
|
if (strlen(display_name) <= EDID_DISPLAY_DESCRIPTOR_DATA_LEN) {
|
|
|
|
memcpy(edid->display_name_descriptor.descriptor_data, display_name, strlen(display_name));
|
|
|
|
if (strlen(display_name) < EDID_DISPLAY_DESCRIPTOR_DATA_LEN) {
|
|
|
|
memset( edid->display_name_descriptor.descriptor_data
|
|
|
|
+ strlen(display_name), 0x0A, 1);
|
|
|
|
memset( edid->display_name_descriptor.descriptor_data
|
|
|
|
+ strlen(display_name) + 1, 0x20, EDID_DISPLAY_DESCRIPTOR_DATA_LEN-strlen(display_name)-1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: Display name is too long.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: Could not parse JSON file somewhere around display_name_descriptor.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_display_serial_number_descriptor(
|
|
|
|
edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
char* serial_number = NULL;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
edid->display_serial_number_descriptor.display_descriptor = 0;
|
|
|
|
edid->display_serial_number_descriptor.reserved0 = 0;
|
|
|
|
edid->display_serial_number_descriptor.descriptor_type = 0xFF;
|
|
|
|
edid->display_serial_number_descriptor.reserved1 = 0;
|
|
|
|
json_scanf( json_str, json_len,
|
|
|
|
"{display_serial_number_descriptor: {serial_number: %Q}}",
|
|
|
|
&serial_number);
|
|
|
|
if (serial_number != NULL) {
|
|
|
|
if (strlen(serial_number) <= EDID_DISPLAY_DESCRIPTOR_DATA_LEN) {
|
|
|
|
memcpy(edid->display_serial_number_descriptor.descriptor_data, serial_number, strlen(serial_number));
|
|
|
|
if (strlen(serial_number) < EDID_DISPLAY_DESCRIPTOR_DATA_LEN) {
|
|
|
|
memset( edid->display_serial_number_descriptor.descriptor_data
|
|
|
|
+ strlen(serial_number), 0x0A, 1);
|
|
|
|
memset( edid->display_serial_number_descriptor.descriptor_data
|
|
|
|
+ strlen(serial_number) + 1, 0x20, EDID_DISPLAY_DESCRIPTOR_DATA_LEN-strlen(serial_number)-1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: Display name is too long.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: Could not parse JSON file somewhere around display_serial_number_descriptor.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int generate_checksum(
|
|
|
|
edid_t* edid,
|
|
|
|
unsigned char* json_str,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
uint8_t* _edid = NULL;
|
|
|
|
unsigned int sum = 0;
|
|
|
|
|
|
|
|
if (edid && json_str) {
|
|
|
|
// Was ist denn mit Carsten los?
|
|
|
|
_edid = (uint8_t*)edid;
|
|
|
|
for (int sanny=0; sanny<EDID_LEN-1; sanny++) {
|
|
|
|
sum += _edid[sanny];
|
|
|
|
}
|
|
|
|
edid->checksum = 256-(sum % 256);
|
|
|
|
} else {
|
|
|
|
printf("Error: One of either edid or json_str is NULL.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|