2020-11-18 00:21:07 +01:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* DDC monitor emulation.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*
|
2020-11-24 01:56:06 -03:00
|
|
|
* Authors: RichardG, <richardg867@gmail.com>
|
2020-11-18 00:21:07 +01:00
|
|
|
*
|
2020-11-20 01:22:04 -03:00
|
|
|
* Copyright 2020 RichardG.
|
2020-11-18 00:21:07 +01:00
|
|
|
*/
|
2020-11-20 01:22:04 -03:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <string.h>
|
2020-11-18 00:21:07 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <wchar.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <86box/86box.h>
|
2020-11-20 01:22:04 -03:00
|
|
|
#include <86box/i2c.h>
|
2020-11-18 00:21:07 +01:00
|
|
|
|
|
|
|
|
|
2020-11-24 01:56:06 -03:00
|
|
|
#define STD_TIMING(idx, width, aspect_ratio) do { \
|
2020-11-25 14:28:26 -03:00
|
|
|
edid->standard_timings[idx].horiz_pixels = ((width) >> 3) - 31; \
|
2020-11-25 00:16:42 -03:00
|
|
|
edid->standard_timings[idx].aspect_ratio_refresh_rate = (aspect_ratio) << 6; /* 60 Hz */ \
|
2020-11-24 01:56:06 -03:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
STD_ASPECT_16_10 = 0x0,
|
|
|
|
|
STD_ASPECT_4_3,
|
|
|
|
|
STD_ASPECT_5_4,
|
|
|
|
|
STD_ASPECT_16_9
|
2020-11-18 00:21:07 +01:00
|
|
|
};
|
|
|
|
|
|
2020-11-24 01:56:06 -03:00
|
|
|
typedef struct {
|
|
|
|
|
uint8_t horiz_pixels, aspect_ratio_refresh_rate;
|
2020-11-25 00:16:42 -03:00
|
|
|
} edid_standard_timing_t;
|
2020-11-24 01:56:06 -03:00
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint8_t pixel_clock_lsb, pixel_clock_msb, h_active_lsb, h_blank_lsb,
|
|
|
|
|
h_active_blank_msb, v_active_lsb, v_blank_lsb, v_active_blank_msb,
|
|
|
|
|
h_front_porch_lsb, h_sync_pulse_lsb, v_front_porch_sync_pulse_lsb,
|
|
|
|
|
hv_front_porch_sync_pulse_msb, h_size_lsb, v_size_lsb, hv_size_msb,
|
|
|
|
|
h_border, v_border, features;
|
|
|
|
|
} edid_detailed_timing_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2020-11-25 00:16:42 -03:00
|
|
|
uint8_t magic[2], reserved, tag, range_limit_offsets;
|
2020-11-24 01:56:06 -03:00
|
|
|
union {
|
|
|
|
|
char ascii[13];
|
2020-11-25 14:28:26 -03:00
|
|
|
struct {
|
|
|
|
|
uint8_t min_v_field, max_v_field, min_h_line, max_h_line, max_pixel_clock,
|
|
|
|
|
timing_type;
|
|
|
|
|
union {
|
|
|
|
|
uint8_t padding[7];
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t reserved, gtf_start_freq, gtf_c, gtf_m_lsb, gtf_m_msb,
|
|
|
|
|
gtf_k, gtf_j;
|
|
|
|
|
};
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t cvt_version, add_clock_precision, max_active_pixels,
|
|
|
|
|
aspect_ratios, aspect_ratio_pref, scaling_support,
|
|
|
|
|
refresh_pref;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
} range_limits;
|
|
|
|
|
struct {
|
|
|
|
|
edid_standard_timing_t standard_timings[6];
|
|
|
|
|
uint8_t padding;
|
|
|
|
|
} ext_standard_timings;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t version;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t lines_lsb, lines_msb_aspect_ratio, refresh_rate;
|
|
|
|
|
} timings[4];
|
|
|
|
|
} cvt_timings;
|
|
|
|
|
struct {
|
|
|
|
|
uint8_t version, timings[6], reserved[6];
|
|
|
|
|
} established_timings3;
|
2020-11-24 01:56:06 -03:00
|
|
|
};
|
|
|
|
|
} edid_descriptor_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint8_t magic[8], mfg[2], mfg_product[2], serial[4], mfg_week, mfg_year,
|
|
|
|
|
edid_version, edid_rev;
|
|
|
|
|
uint8_t input_params, horiz_size, vert_size, gamma, features;
|
2020-11-25 00:16:42 -03:00
|
|
|
uint8_t red_green_lsb, blue_white_lsb, red_x_msb, red_y_msb, green_x_msb,
|
|
|
|
|
green_y_msb, blue_x_msb, blue_y_msb, white_x_msb, white_y_msb;
|
|
|
|
|
uint8_t established_timings[3];
|
|
|
|
|
edid_standard_timing_t standard_timings[8];
|
2020-11-24 01:56:06 -03:00
|
|
|
union {
|
|
|
|
|
edid_detailed_timing_t detailed_timings[4];
|
|
|
|
|
edid_descriptor_t descriptors[4];
|
|
|
|
|
};
|
|
|
|
|
uint8_t extensions, checksum;
|
|
|
|
|
|
2020-11-25 00:16:42 -03:00
|
|
|
uint8_t ext_tag, ext_rev, ext_dtd_offset, ext_native_dtds;
|
|
|
|
|
union {
|
|
|
|
|
edid_detailed_timing_t ext_detailed_timings[6];
|
|
|
|
|
edid_descriptor_t ext_descriptors[6];
|
|
|
|
|
};
|
|
|
|
|
uint8_t padding[15], checksum2;
|
|
|
|
|
} edid_t;
|
2020-11-24 01:56:06 -03:00
|
|
|
|
2020-11-18 00:21:07 +01:00
|
|
|
|
2020-11-20 19:23:14 -03:00
|
|
|
void *
|
2020-11-20 01:22:04 -03:00
|
|
|
ddc_init(void *i2c)
|
|
|
|
|
{
|
2020-11-25 00:16:42 -03:00
|
|
|
edid_t *edid = malloc(sizeof(edid_t));
|
|
|
|
|
memset(edid, 0, sizeof(edid_t));
|
|
|
|
|
|
|
|
|
|
memset(&edid->magic[1], 0xff, sizeof(edid->magic) - 2);
|
|
|
|
|
|
|
|
|
|
edid->mfg[0] = 0x09; /* manufacturer "BOX" (apparently unassigned by UEFI) */
|
|
|
|
|
edid->mfg[1] = 0xf8;
|
|
|
|
|
edid->mfg_week = 48;
|
|
|
|
|
edid->mfg_year = 2020 - 1990;
|
|
|
|
|
edid->edid_version = 0x01;
|
|
|
|
|
edid->edid_rev = 0x04; /* EDID 1.4 */
|
|
|
|
|
|
|
|
|
|
edid->input_params = 0x0e; /* analog input; separate sync; composite sync; sync on green */
|
|
|
|
|
edid->horiz_size = ((4.0 / 3.0) * 100) - 99; /* landscape 4:3 */
|
|
|
|
|
edid->features = 0x09; /* RGB color; GTF/CVT */
|
|
|
|
|
|
|
|
|
|
edid->red_green_lsb = 0x81;
|
|
|
|
|
edid->blue_white_lsb = 0xf1;
|
|
|
|
|
edid->red_x_msb = 0xa3;
|
|
|
|
|
edid->red_y_msb = 0x57;
|
|
|
|
|
edid->green_x_msb = 0x53;
|
|
|
|
|
edid->green_y_msb = 0x9f;
|
|
|
|
|
edid->blue_x_msb = 0x27;
|
|
|
|
|
edid->blue_y_msb = 0x0a;
|
|
|
|
|
edid->white_x_msb = 0x50;
|
|
|
|
|
edid->white_y_msb = 0x00;
|
|
|
|
|
|
|
|
|
|
memset(&edid->established_timings, 0xff, sizeof(edid->established_timings)); /* all enabled */
|
|
|
|
|
|
|
|
|
|
memset(&edid->standard_timings, 0x01, sizeof(edid->standard_timings)); /* pad unused entries */
|
|
|
|
|
STD_TIMING(0, 1280, STD_ASPECT_16_9); /* 1280x720 */
|
2020-11-24 01:56:06 -03:00
|
|
|
STD_TIMING(1, 1280, STD_ASPECT_16_10); /* 1280x800 */
|
|
|
|
|
STD_TIMING(2, 1366, STD_ASPECT_16_9); /* 1360x768 (closest to 1366x768) */
|
2020-11-25 00:16:42 -03:00
|
|
|
STD_TIMING(3, 1440, STD_ASPECT_16_10); /* 1440x900 */
|
|
|
|
|
STD_TIMING(4, 1600, STD_ASPECT_16_9); /* 1600x900 */
|
|
|
|
|
STD_TIMING(5, 1680, STD_ASPECT_16_10); /* 1680x1050 */
|
|
|
|
|
STD_TIMING(6, 1920, STD_ASPECT_16_9); /* 1920x1080 */
|
|
|
|
|
STD_TIMING(7, 2048, STD_ASPECT_4_3); /* 2048x1536 */
|
|
|
|
|
|
|
|
|
|
/* Detailed timings for the preferred mode of 800x600 */
|
2020-11-25 17:04:35 -03:00
|
|
|
edid->detailed_timings[0].pixel_clock_lsb = 4000 & 0xff; /* 40.000 MHz */
|
|
|
|
|
edid->detailed_timings[0].pixel_clock_msb = 4000 >> 8;
|
2020-11-25 00:16:42 -03:00
|
|
|
edid->detailed_timings[0].h_active_lsb = 800 & 0xff;
|
|
|
|
|
edid->detailed_timings[0].h_blank_lsb = 256 & 0xff;
|
|
|
|
|
edid->detailed_timings[0].h_active_blank_msb = ((800 >> 4) & 0xf0) | ((256 >> 8) & 0x0f);
|
|
|
|
|
edid->detailed_timings[0].v_active_lsb = 600 & 0xff;
|
|
|
|
|
edid->detailed_timings[0].v_blank_lsb = 28;
|
|
|
|
|
edid->detailed_timings[0].v_active_blank_msb = (600 >> 4) & 0xf0;
|
|
|
|
|
edid->detailed_timings[0].h_front_porch_lsb = 40;
|
|
|
|
|
edid->detailed_timings[0].h_sync_pulse_lsb = 128;
|
|
|
|
|
edid->detailed_timings[0].v_front_porch_sync_pulse_lsb = (1 << 4) | 4;
|
|
|
|
|
|
|
|
|
|
edid->descriptors[1].tag = 0xf7; /* established timings 3 */
|
|
|
|
|
edid->descriptors[1].established_timings3.version = 0x0a;
|
|
|
|
|
memset(&edid->descriptors[1].established_timings3.timings, 0xff, sizeof(edid->descriptors[1].established_timings3.timings)); /* all enabled */
|
|
|
|
|
|
|
|
|
|
edid->descriptors[2].tag = 0xfc; /* display name */
|
|
|
|
|
memcpy(&edid->descriptors[2].ascii, "86Box Monitor", 13); /* exactly 13 characters (would otherwise require LF termination and space padding) */
|
|
|
|
|
|
|
|
|
|
edid->descriptors[3].tag = 0xfd; /* range limits */
|
|
|
|
|
edid->descriptors[3].range_limits.min_v_field = 1;
|
|
|
|
|
edid->descriptors[3].range_limits.max_v_field = -1;
|
|
|
|
|
edid->descriptors[3].range_limits.min_h_line = 1;
|
|
|
|
|
edid->descriptors[3].range_limits.max_h_line = -1;
|
|
|
|
|
edid->descriptors[3].range_limits.max_pixel_clock = -1;
|
|
|
|
|
edid->descriptors[3].range_limits.timing_type = 0x00; /* default GTF */
|
|
|
|
|
edid->descriptors[3].range_limits.padding[0] = 0x0a;
|
|
|
|
|
memset(&edid->descriptors[3].range_limits.padding[1], 0x20, sizeof(edid->descriptors[3].range_limits.padding) - 1);
|
|
|
|
|
|
|
|
|
|
uint8_t *edid_bytes = (uint8_t *) edid;
|
2020-11-24 01:56:06 -03:00
|
|
|
for (uint8_t c = 0; c < 127; c++)
|
2020-11-25 00:16:42 -03:00
|
|
|
edid->checksum += edid_bytes[c];
|
|
|
|
|
edid->checksum = 256 - edid->checksum;
|
|
|
|
|
for (uint8_t c = 128; c < 255; c++)
|
|
|
|
|
edid->checksum2 += edid_bytes[c];
|
|
|
|
|
edid->checksum2 = 256 - edid->checksum2;
|
2020-11-20 01:22:04 -03:00
|
|
|
|
2020-11-25 00:16:42 -03:00
|
|
|
return i2c_eeprom_init(i2c, 0x50, edid_bytes, sizeof(edid_t), 0);
|
2020-11-20 19:23:14 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2020-11-25 00:16:42 -03:00
|
|
|
ddc_close(void *eeprom)
|
2020-11-20 19:23:14 -03:00
|
|
|
{
|
2020-11-25 00:16:42 -03:00
|
|
|
i2c_eeprom_close(eeprom);
|
2020-11-18 00:21:07 +01:00
|
|
|
}
|