Files
86Box/src/video/vid_ddc.c

190 lines
6.2 KiB
C
Raw Normal View History

/*
* 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.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2020 RichardG.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <wchar.h>
#include <math.h>
#include <86box/86box.h>
#include <86box/i2c.h>
#define STD_TIMING(idx, width, aspect_ratio) do { \
edid.std_timings[idx].horiz_pixels = ((width) / 8) - 31; \
edid.std_timings[idx].aspect_ratio_refresh_rate = (aspect_ratio) << 6; /* 60 Hz */ \
} while (0)
enum {
STD_ASPECT_16_10 = 0x0,
STD_ASPECT_4_3,
STD_ASPECT_5_4,
STD_ASPECT_16_9
};
typedef struct {
uint8_t horiz_pixels, aspect_ratio_refresh_rate;
} edid_std_timing_t;
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 {
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;
};
};
} edid_range_limits_t;
typedef struct {
uint8_t version, timings[6], reserved[6];
} edid_established_timings3_t;
typedef struct {
uint8_t version;
struct {
uint8_t lines_lsb, lines_msb_aspect_ratio, refresh_rate;
} timings[4];
} edid_cvt_timings_t;
typedef struct {
uint8_t magic[2], reserved, type, range_limit_offsets;
union {
char ascii[13];
edid_range_limits_t range_limits;
edid_established_timings3_t established_timings3;
edid_cvt_timings_t cvt_timings;
};
} 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;
uint8_t chromaticity[10], established_timings[3];
edid_std_timing_t std_timings[8];
union {
edid_detailed_timing_t detailed_timings[4];
edid_descriptor_t descriptors[4];
};
uint8_t extensions, checksum;
} edid_t;
static edid_t edid;
2020-11-20 19:23:14 -03:00
void *
ddc_init(void *i2c)
{
memset(&edid.magic[1], 0xff, sizeof(edid.magic) - 2);
edid.magic[0] = edid.magic[7] = 0x00;
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 = 0x03; /* EDID 1.3 */
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; CVT */
edid.chromaticity[0] = 0x81;
edid.chromaticity[1] = 0xf1;
edid.chromaticity[2] = 0xa3;
edid.chromaticity[3] = 0x57;
edid.chromaticity[4] = 0x53;
edid.chromaticity[5] = 0x9f;
edid.chromaticity[6] = 0x27;
edid.chromaticity[7] = 0x0a;
edid.chromaticity[8] = 0x50;
edid.chromaticity[9] = 0x00;
memset(&edid.established_timings, 0xff, sizeof(edid.established_timings)); /* all enabled */
memset(&edid.std_timings, 0x01, sizeof(edid.std_timings)); /* pad unused entries with 0x01 */
STD_TIMING(0, 800, STD_ASPECT_4_3); /* 800x600 (preferred) */
STD_TIMING(1, 1280, STD_ASPECT_16_9); /* 1280x720 */
STD_TIMING(1, 1280, STD_ASPECT_16_10); /* 1280x800 */
STD_TIMING(2, 1366, STD_ASPECT_16_9); /* 1360x768 (closest to 1366x768) */
STD_TIMING(3, 1600, STD_ASPECT_16_9); /* 1600x900 */
STD_TIMING(4, 1920, STD_ASPECT_16_9); /* 1920x1080 */
STD_TIMING(5, 2048, STD_ASPECT_4_3); /* 2048x1536 */
/* Detailed timings for the preferred resolution of 800x600 */
edid.detailed_timings[0].pixel_clock_lsb = 0xa0; /* 40000 KHz */
edid.detailed_timings[0].pixel_clock_msb = 0x0f;
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].type = 0xf7; /* established timings 3 */
edid.descriptors[1].established_timings3.version = 0x10;
memset(&edid.descriptors[1].established_timings3.timings, 0xff, sizeof(edid.descriptors[1].established_timings3.timings)); /* all enabled */
edid.descriptors[2].type = 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].type = 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_data = (uint8_t *) &edid;
for (uint8_t c = 0; c < 127; c++)
edid.checksum += edid_data[c];
edid.checksum = 256 - edid.checksum;
return i2c_eeprom_init(i2c, 0x50, edid_data, sizeof(edid), 0);
2020-11-20 19:23:14 -03:00
}
void
ddc_close(void *dev_handle)
{
i2c_eeprom_close(dev_handle);
}