1409f1731Sshadow/*
27b48d975SAxel Dörfler * Copyright 2003, Thomas Kurschel. All Rights Reserved.
3047165ceSAxel Dörfler * Distributed under the terms of the MIT License.
4047165ceSAxel Dörfler */
5409f1731Sshadow
67b48d975SAxel Dörfler
7047165ceSAxel Dörfler/*!
8409f1731Sshadow	Part of DDC driver
9047165ceSAxel Dörfler
102a37e4c1Sshadow	EDID decoder.
11047165ceSAxel Dörfler
122a37e4c1Sshadow	The EDID information is tightly packed; this file takes care of
132a37e4c1Sshadow	converting it to a usable structure.
14409f1731Sshadow*/
15409f1731Sshadow
16047165ceSAxel Dörfler
17409f1731Sshadow#include "edid.h"
18f1994d8bSAxel Dörfler
19409f1731Sshadow#include <KernelExport.h>
207b48d975SAxel Dörfler
21f1994d8bSAxel Dörfler#include <string.h>
22047165ceSAxel Dörfler
23409f1731Sshadow
24409f1731Sshadow//
25409f1731Sshadow// from hereon a bunch of decoders follow for each EDID section
26409f1731Sshadow//
27409f1731Sshadow
28047165ceSAxel Dörflerstatic void
29047165ceSAxel Dörflerdecode_vendor(edid1_vendor *vendor, const edid1_vendor_raw *raw)
30409f1731Sshadow{
31409f1731Sshadow	vendor->manufacturer[0] = raw->c1 + '@';
32047165ceSAxel Dörfler	vendor->manufacturer[1] = ((raw->c2_high << 3) | raw->c2_low) + '@';
33409f1731Sshadow	vendor->manufacturer[2] = raw->c3 + '@';
34409f1731Sshadow	vendor->manufacturer[3] = 0;
35047165ceSAxel Dörfler	vendor->prod_id = B_LENDIAN_TO_HOST_INT16(raw->prod_id);
36047165ceSAxel Dörfler	vendor->serial = B_LENDIAN_TO_HOST_INT32(raw->serial);
37409f1731Sshadow	vendor->week = raw->week;
38409f1731Sshadow	vendor->year = raw->year + 1990;
39409f1731Sshadow}
40409f1731Sshadow
41047165ceSAxel Dörfler
42047165ceSAxel Dörflerstatic void
43047165ceSAxel Dörflerdecode_version(edid1_version *version, const edid1_version_raw *raw)
44409f1731Sshadow{
45409f1731Sshadow	version->version = raw->version;
46409f1731Sshadow	version->revision = raw->revision;
47409f1731Sshadow}
48409f1731Sshadow
49047165ceSAxel Dörfler
50047165ceSAxel Dörflerstatic void
51047165ceSAxel Dörflerdecode_display(edid1_display *display, const edid1_display_raw *raw)
52409f1731Sshadow{
53409f1731Sshadow	display->input_type = raw->input_type;
54409f1731Sshadow	display->input_voltage = raw->input_voltage;
55409f1731Sshadow	display->setup = raw->setup;
56409f1731Sshadow	display->sep_sync = raw->sep_sync;
57409f1731Sshadow	display->comp_sync = raw->comp_sync;
58409f1731Sshadow	display->sync_on_green = raw->sync_on_green;
59409f1731Sshadow	display->sync_serr = raw->sync_serr;
60047165ceSAxel Dörfler
61409f1731Sshadow	display->h_size = raw->h_size;
62409f1731Sshadow	display->v_size = raw->v_size;
63409f1731Sshadow	display->gamma = raw->gamma;
64409f1731Sshadow
65409f1731Sshadow	display->dpms_standby = raw->dpms_standby;
66409f1731Sshadow	display->dpms_suspend = raw->dpms_suspend;
67409f1731Sshadow	display->dpms_off = raw->dpms_off;
68409f1731Sshadow	display->display_type = raw->display_type;
69409f1731Sshadow	display->std_colour_space = raw->std_colour_space;
70409f1731Sshadow	display->preferred_timing_mode = raw->preferred_timing_mode;
71409f1731Sshadow	display->gtf_supported = raw->gtf_supported;
72409f1731Sshadow
73409f1731Sshadow	display->red_x = ((uint16)raw->red_x << 2) | raw->red_x_low;
74409f1731Sshadow	display->red_y = ((uint16)raw->red_y << 2) | raw->red_y_low;
75409f1731Sshadow	display->green_x = ((uint16)raw->green_x << 2) | raw->green_x_low;
76409f1731Sshadow	display->green_y = ((uint16)raw->green_y << 2) | raw->green_y_low;
77409f1731Sshadow	display->blue_x = ((uint16)raw->blue_x << 2) | raw->blue_x_low;
78409f1731Sshadow	display->blue_y = ((uint16)raw->blue_y << 2) | raw->blue_y_low;
79409f1731Sshadow	display->white_x = ((uint16)raw->white_x << 2) | raw->white_x_low;
80409f1731Sshadow	display->white_y = ((uint16)raw->white_y << 2) | raw->white_y_low;
81409f1731Sshadow}
82409f1731Sshadow
83047165ceSAxel Dörfler
84047165ceSAxel Dörflerstatic void
85047165ceSAxel Dörflerdecode_std_timing(edid1_std_timing *timing, const edid1_std_timing_raw *raw)
86409f1731Sshadow{
87409f1731Sshadow	timing->h_size = (raw->timing.h_size + 31) * 8;
88409f1731Sshadow	timing->ratio = raw->timing.ratio;
89047165ceSAxel Dörfler
90047165ceSAxel Dörfler	switch (raw->timing.ratio) {
91047165ceSAxel Dörfler		case 0:
92047165ceSAxel Dörfler			timing->v_size = timing->h_size;
93047165ceSAxel Dörfler			break;
947b48d975SAxel Dörfler
95047165ceSAxel Dörfler		case 1:
96047165ceSAxel Dörfler			timing->v_size = timing->h_size * 3 / 4;
97047165ceSAxel Dörfler			break;
987b48d975SAxel Dörfler
99047165ceSAxel Dörfler		case 2:
100047165ceSAxel Dörfler			timing->v_size = timing->h_size * 4 / 5;
101047165ceSAxel Dörfler			break;
1027b48d975SAxel Dörfler
103047165ceSAxel Dörfler		case 3:
104047165ceSAxel Dörfler			timing->v_size = timing->h_size * 9 / 16;
105047165ceSAxel Dörfler			break;
106409f1731Sshadow	}
107409f1731Sshadow	timing->refresh = raw->timing.refresh + 60;
108409f1731Sshadow	timing->id = raw->id;
109409f1731Sshadow}
110409f1731Sshadow
111047165ceSAxel Dörfler
112047165ceSAxel Dörflerstatic void
113047165ceSAxel Dörflerdecode_whitepoint(edid1_whitepoint *whitepoint, const edid1_whitepoint_raw *raw)
114409f1731Sshadow{
115409f1731Sshadow	whitepoint[0].index = raw->index1;
116409f1731Sshadow	whitepoint[0].white_x = ((uint16)raw->white_x1 << 2) | raw->white_x1_low;
117409f1731Sshadow	whitepoint[0].white_y = ((uint16)raw->white_y1 << 2) | raw->white_y1_low;
118409f1731Sshadow	whitepoint[0].gamma = raw->gamma1;
119047165ceSAxel Dörfler
120409f1731Sshadow	whitepoint[1].index = raw->index2;
121409f1731Sshadow	whitepoint[1].white_x = ((uint16)raw->white_x2 << 2) | raw->white_x2_low;
122409f1731Sshadow	whitepoint[1].white_y = ((uint16)raw->white_y2 << 2) | raw->white_y2_low;
123409f1731Sshadow	whitepoint[1].gamma = raw->gamma2;
124409f1731Sshadow}
125409f1731Sshadow
126047165ceSAxel Dörfler
127047165ceSAxel Dörflerstatic void
1287b48d975SAxel Dörflerdecode_detailed_timing(edid1_detailed_timing *timing,
129047165ceSAxel Dörfler	const edid1_detailed_timing_raw *raw)
130409f1731Sshadow{
131409f1731Sshadow	timing->pixel_clock = raw->pixel_clock;
132409f1731Sshadow	timing->h_active = ((uint16)raw->h_active_high << 8) | raw->h_active;
133409f1731Sshadow	timing->h_blank = ((uint16)raw->h_blank_high << 8) | raw->h_blank;
134409f1731Sshadow	timing->v_active = ((uint16)raw->v_active_high << 8) | raw->v_active;
135409f1731Sshadow	timing->v_blank = ((uint16)raw->v_blank_high << 8) | raw->v_blank;
136409f1731Sshadow	timing->h_sync_off = ((uint16)raw->h_sync_off_high << 8) | raw->h_sync_off;
137409f1731Sshadow	timing->h_sync_width = ((uint16)raw->h_sync_width_high << 8) | raw->h_sync_width;
138409f1731Sshadow	timing->v_sync_off = ((uint16)raw->v_sync_off_high << 4) | raw->v_sync_off;
139409f1731Sshadow	timing->v_sync_width = ((uint16)raw->v_sync_width_high << 4) | raw->v_sync_width;
140409f1731Sshadow	timing->h_size = ((uint16)raw->h_size_high << 8) | raw->h_size;
141409f1731Sshadow	timing->v_size = ((uint16)raw->v_size_high << 8) | raw->v_size;
142409f1731Sshadow	timing->h_border = raw->h_border;
143409f1731Sshadow	timing->v_border = raw->v_border;
144409f1731Sshadow	timing->interlaced = raw->interlaced;
145409f1731Sshadow	timing->stereo = raw->stereo;
146409f1731Sshadow	timing->sync = raw->sync;
147409f1731Sshadow	timing->misc = raw->misc;
148409f1731Sshadow}
149409f1731Sshadow
150047165ceSAxel Dörfler
151047165ceSAxel Dörfler//! copy string until 0xa, removing trailing spaces
152047165ceSAxel Dörflerstatic void
153047165ceSAxel Dörflercopy_str(char *dest, const uint8 *src, size_t len)
154409f1731Sshadow{
155047165ceSAxel Dörfler	uint32 i;
156047165ceSAxel Dörfler
157409f1731Sshadow	// copy until 0xa
158c54a6536SAxel Dörfler	for (i = 0; i < len; i++) {
159047165ceSAxel Dörfler		if (*src == 0xa)
160409f1731Sshadow			break;
161047165ceSAxel Dörfler
162409f1731Sshadow		*dest++ = *src++;
163409f1731Sshadow	}
164047165ceSAxel Dörfler
165409f1731Sshadow	// remove trailing spaces
166c54a6536SAxel Dörfler	while (i-- > 0) {
167c54a6536SAxel Dörfler		if (*(dest - 1) != ' ')
168409f1731Sshadow			break;
169c54a6536SAxel Dörfler
170c54a6536SAxel Dörfler		dest--;
171409f1731Sshadow	}
172047165ceSAxel Dörfler
173c54a6536SAxel Dörfler	*dest = '\0';
174409f1731Sshadow}
175409f1731Sshadow
176047165ceSAxel Dörfler
177047165ceSAxel Dörflerstatic void
1787b48d975SAxel Dörflerdecode_detailed_monitor(edid1_detailed_monitor *monitor,
179047165ceSAxel Dörfler	const edid1_detailed_monitor_raw *raw, bool enableExtra)
180409f1731Sshadow{
181409f1731Sshadow	int i, j;
182409f1731Sshadow
183047165ceSAxel Dörfler	for (i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i, ++monitor, ++raw) {
184047165ceSAxel Dörfler
185409f1731Sshadow		// workaround: normally, all four bytes must be zero for detailed
186409f1731Sshadow		// description, but at least some Formac monitors violate that:
187409f1731Sshadow		// they have some additional info that start at zero_4(!),
188409f1731Sshadow		// so even if only the first two _or_ the other two bytes are
189409f1731Sshadow		// zero, we accept it as a monitor description block
190047165ceSAxel Dörfler		if (enableExtra
191047165ceSAxel Dörfler			&& ((raw->extra.zero_0[0] == 0 && raw->extra.zero_0[1] == 0)
192047165ceSAxel Dörfler				|| (raw->extra.zero_0[2] == 0 && raw->extra.zero_4 == 0))) {
193409f1731Sshadow			monitor->monitor_desc_type = raw->extra.monitor_desc_type;
194047165ceSAxel Dörfler
195047165ceSAxel Dörfler			switch (raw->extra.monitor_desc_type) {
19613a81299SAxel Dörfler				case EDID1_SERIAL_NUMBER:
19713a81299SAxel Dörfler					copy_str(monitor->data.serial_number,
19813a81299SAxel Dörfler						raw->extra.data.serial_number, EDID1_EXTRA_STRING_LEN);
199047165ceSAxel Dörfler					break;
2007b48d975SAxel Dörfler
20113a81299SAxel Dörfler				case EDID1_ASCII_DATA:
20213a81299SAxel Dörfler					copy_str(monitor->data.ascii_data,
20313a81299SAxel Dörfler						raw->extra.data.ascii_data, EDID1_EXTRA_STRING_LEN);
204047165ceSAxel Dörfler					break;
2057b48d975SAxel Dörfler
20613a81299SAxel Dörfler				case EDID1_MONITOR_RANGES:
207047165ceSAxel Dörfler					monitor->data.monitor_range = raw->extra.data.monitor_range;
208047165ceSAxel Dörfler					break;
2097b48d975SAxel Dörfler
21013a81299SAxel Dörfler				case EDID1_MONITOR_NAME:
21113a81299SAxel Dörfler					copy_str(monitor->data.monitor_name,
21213a81299SAxel Dörfler						raw->extra.data.monitor_name, EDID1_EXTRA_STRING_LEN);
213047165ceSAxel Dörfler					break;
2147b48d975SAxel Dörfler
21513a81299SAxel Dörfler				case EDID1_ADD_COLOUR_POINTER:
21613a81299SAxel Dörfler					decode_whitepoint(monitor->data.whitepoint,
21713a81299SAxel Dörfler						&raw->extra.data.whitepoint);
218047165ceSAxel Dörfler					break;
2197b48d975SAxel Dörfler
22013a81299SAxel Dörfler				case EDID1_ADD_STD_TIMING:
221047165ceSAxel Dörfler					for (j = 0; j < EDID1_NUM_EXTRA_STD_TIMING; ++j) {
222047165ceSAxel Dörfler						decode_std_timing(&monitor->data.std_timing[j],
223047165ceSAxel Dörfler							&raw->extra.data.std_timing[j]);
224047165ceSAxel Dörfler					}
225047165ceSAxel Dörfler					break;
226409f1731Sshadow			}
2279661f324SIthamar R. Adema		} else if (raw->detailed_timing.pixel_clock > 0) {
2289661f324SIthamar R. Adema			monitor->monitor_desc_type = EDID1_IS_DETAILED_TIMING;
229047165ceSAxel Dörfler			decode_detailed_timing(&monitor->data.detailed_timing,
230047165ceSAxel Dörfler				&raw->detailed_timing);
231409f1731Sshadow		}
232409f1731Sshadow	}
233409f1731Sshadow}
234409f1731Sshadow
235047165ceSAxel Dörfler
236047165ceSAxel Dörfler//	#pragma mark -
237047165ceSAxel Dörfler
238047165ceSAxel Dörfler
2397b48d975SAxel Dörfler//!	Main function to decode edid data
240047165ceSAxel Dörflervoid
241047165ceSAxel Dörfleredid_decode(edid1_info *edid, const edid1_raw *raw)
242409f1731Sshadow{
243409f1731Sshadow	int i;
2444462c768SMichael Lotz	memset(edid, 0, sizeof(edid1_info));
245047165ceSAxel Dörfler
246047165ceSAxel Dörfler	decode_vendor(&edid->vendor, &raw->vendor);
247047165ceSAxel Dörfler	decode_version(&edid->version, &raw->version);
248047165ceSAxel Dörfler	decode_display(&edid->display, &raw->display);
249409f1731Sshadow
250409f1731Sshadow	edid->established_timing = raw->established_timing;
251409f1731Sshadow
252047165ceSAxel Dörfler	for (i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
253047165ceSAxel Dörfler		decode_std_timing(&edid->std_timing[i], &raw->std_timing[i]);
254047165ceSAxel Dörfler	}
255409f1731Sshadow
256047165ceSAxel Dörfler	decode_detailed_monitor(edid->detailed_monitor, raw->detailed_monitor,
257047165ceSAxel Dörfler		edid->version.version == 1 && edid->version.revision >= 1);
258409f1731Sshadow}
259