1/*
2 * Copyright 2003, Thomas Kurschel. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7/*!
8	Part of DDC driver
9
10	EDID decoder.
11
12	The EDID information is tightly packed; this file takes care of
13	converting it to a usable structure.
14*/
15
16
17#include "edid.h"
18
19#include <KernelExport.h>
20
21#include <string.h>
22
23
24//
25// from hereon a bunch of decoders follow for each EDID section
26//
27
28static void
29decode_vendor(edid1_vendor *vendor, const edid1_vendor_raw *raw)
30{
31	vendor->manufacturer[0] = raw->c1 + '@';
32	vendor->manufacturer[1] = ((raw->c2_high << 3) | raw->c2_low) + '@';
33	vendor->manufacturer[2] = raw->c3 + '@';
34	vendor->manufacturer[3] = 0;
35	vendor->prod_id = B_LENDIAN_TO_HOST_INT16(raw->prod_id);
36	vendor->serial = B_LENDIAN_TO_HOST_INT32(raw->serial);
37	vendor->week = raw->week;
38	vendor->year = raw->year + 1990;
39}
40
41
42static void
43decode_version(edid1_version *version, const edid1_version_raw *raw)
44{
45	version->version = raw->version;
46	version->revision = raw->revision;
47}
48
49
50static void
51decode_display(edid1_display *display, const edid1_display_raw *raw)
52{
53	display->input_type = raw->input_type;
54	display->input_voltage = raw->input_voltage;
55	display->setup = raw->setup;
56	display->sep_sync = raw->sep_sync;
57	display->comp_sync = raw->comp_sync;
58	display->sync_on_green = raw->sync_on_green;
59	display->sync_serr = raw->sync_serr;
60
61	display->h_size = raw->h_size;
62	display->v_size = raw->v_size;
63	display->gamma = raw->gamma;
64
65	display->dpms_standby = raw->dpms_standby;
66	display->dpms_suspend = raw->dpms_suspend;
67	display->dpms_off = raw->dpms_off;
68	display->display_type = raw->display_type;
69	display->std_colour_space = raw->std_colour_space;
70	display->preferred_timing_mode = raw->preferred_timing_mode;
71	display->gtf_supported = raw->gtf_supported;
72
73	display->red_x = ((uint16)raw->red_x << 2) | raw->red_x_low;
74	display->red_y = ((uint16)raw->red_y << 2) | raw->red_y_low;
75	display->green_x = ((uint16)raw->green_x << 2) | raw->green_x_low;
76	display->green_y = ((uint16)raw->green_y << 2) | raw->green_y_low;
77	display->blue_x = ((uint16)raw->blue_x << 2) | raw->blue_x_low;
78	display->blue_y = ((uint16)raw->blue_y << 2) | raw->blue_y_low;
79	display->white_x = ((uint16)raw->white_x << 2) | raw->white_x_low;
80	display->white_y = ((uint16)raw->white_y << 2) | raw->white_y_low;
81}
82
83
84static void
85decode_std_timing(edid1_std_timing *timing, const edid1_std_timing_raw *raw)
86{
87	timing->h_size = (raw->timing.h_size + 31) * 8;
88	timing->ratio = raw->timing.ratio;
89
90	switch (raw->timing.ratio) {
91		case 0:
92			timing->v_size = timing->h_size;
93			break;
94
95		case 1:
96			timing->v_size = timing->h_size * 3 / 4;
97			break;
98
99		case 2:
100			timing->v_size = timing->h_size * 4 / 5;
101			break;
102
103		case 3:
104			timing->v_size = timing->h_size * 9 / 16;
105			break;
106	}
107	timing->refresh = raw->timing.refresh + 60;
108	timing->id = raw->id;
109}
110
111
112static void
113decode_whitepoint(edid1_whitepoint *whitepoint, const edid1_whitepoint_raw *raw)
114{
115	whitepoint[0].index = raw->index1;
116	whitepoint[0].white_x = ((uint16)raw->white_x1 << 2) | raw->white_x1_low;
117	whitepoint[0].white_y = ((uint16)raw->white_y1 << 2) | raw->white_y1_low;
118	whitepoint[0].gamma = raw->gamma1;
119
120	whitepoint[1].index = raw->index2;
121	whitepoint[1].white_x = ((uint16)raw->white_x2 << 2) | raw->white_x2_low;
122	whitepoint[1].white_y = ((uint16)raw->white_y2 << 2) | raw->white_y2_low;
123	whitepoint[1].gamma = raw->gamma2;
124}
125
126
127static void
128decode_detailed_timing(edid1_detailed_timing *timing,
129	const edid1_detailed_timing_raw *raw)
130{
131	timing->pixel_clock = raw->pixel_clock;
132	timing->h_active = ((uint16)raw->h_active_high << 8) | raw->h_active;
133	timing->h_blank = ((uint16)raw->h_blank_high << 8) | raw->h_blank;
134	timing->v_active = ((uint16)raw->v_active_high << 8) | raw->v_active;
135	timing->v_blank = ((uint16)raw->v_blank_high << 8) | raw->v_blank;
136	timing->h_sync_off = ((uint16)raw->h_sync_off_high << 8) | raw->h_sync_off;
137	timing->h_sync_width = ((uint16)raw->h_sync_width_high << 8) | raw->h_sync_width;
138	timing->v_sync_off = ((uint16)raw->v_sync_off_high << 4) | raw->v_sync_off;
139	timing->v_sync_width = ((uint16)raw->v_sync_width_high << 4) | raw->v_sync_width;
140	timing->h_size = ((uint16)raw->h_size_high << 8) | raw->h_size;
141	timing->v_size = ((uint16)raw->v_size_high << 8) | raw->v_size;
142	timing->h_border = raw->h_border;
143	timing->v_border = raw->v_border;
144	timing->interlaced = raw->interlaced;
145	timing->stereo = raw->stereo;
146	timing->sync = raw->sync;
147	timing->misc = raw->misc;
148}
149
150
151//! copy string until 0xa, removing trailing spaces
152static void
153copy_str(char *dest, const uint8 *src, size_t len)
154{
155	uint32 i;
156
157	// copy until 0xa
158	for (i = 0; i < len; i++) {
159		if (*src == 0xa)
160			break;
161
162		*dest++ = *src++;
163	}
164
165	// remove trailing spaces
166	while (i-- > 0) {
167		if (*(dest - 1) != ' ')
168			break;
169
170		dest--;
171	}
172
173	*dest = '\0';
174}
175
176
177static void
178decode_detailed_monitor(edid1_detailed_monitor *monitor,
179	const edid1_detailed_monitor_raw *raw, bool enableExtra)
180{
181	int i, j;
182
183	for (i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i, ++monitor, ++raw) {
184
185		// workaround: normally, all four bytes must be zero for detailed
186		// description, but at least some Formac monitors violate that:
187		// they have some additional info that start at zero_4(!),
188		// so even if only the first two _or_ the other two bytes are
189		// zero, we accept it as a monitor description block
190		if (enableExtra
191			&& ((raw->extra.zero_0[0] == 0 && raw->extra.zero_0[1] == 0)
192				|| (raw->extra.zero_0[2] == 0 && raw->extra.zero_4 == 0))) {
193			monitor->monitor_desc_type = raw->extra.monitor_desc_type;
194
195			switch (raw->extra.monitor_desc_type) {
196				case EDID1_SERIAL_NUMBER:
197					copy_str(monitor->data.serial_number,
198						raw->extra.data.serial_number, EDID1_EXTRA_STRING_LEN);
199					break;
200
201				case EDID1_ASCII_DATA:
202					copy_str(monitor->data.ascii_data,
203						raw->extra.data.ascii_data, EDID1_EXTRA_STRING_LEN);
204					break;
205
206				case EDID1_MONITOR_RANGES:
207					monitor->data.monitor_range = raw->extra.data.monitor_range;
208					break;
209
210				case EDID1_MONITOR_NAME:
211					copy_str(monitor->data.monitor_name,
212						raw->extra.data.monitor_name, EDID1_EXTRA_STRING_LEN);
213					break;
214
215				case EDID1_ADD_COLOUR_POINTER:
216					decode_whitepoint(monitor->data.whitepoint,
217						&raw->extra.data.whitepoint);
218					break;
219
220				case EDID1_ADD_STD_TIMING:
221					for (j = 0; j < EDID1_NUM_EXTRA_STD_TIMING; ++j) {
222						decode_std_timing(&monitor->data.std_timing[j],
223							&raw->extra.data.std_timing[j]);
224					}
225					break;
226			}
227		} else if (raw->detailed_timing.pixel_clock > 0) {
228			monitor->monitor_desc_type = EDID1_IS_DETAILED_TIMING;
229			decode_detailed_timing(&monitor->data.detailed_timing,
230				&raw->detailed_timing);
231		}
232	}
233}
234
235
236//	#pragma mark -
237
238
239//!	Main function to decode edid data
240void
241edid_decode(edid1_info *edid, const edid1_raw *raw)
242{
243	int i;
244	memset(edid, 0, sizeof(edid1_info));
245
246	decode_vendor(&edid->vendor, &raw->vendor);
247	decode_version(&edid->version, &raw->version);
248	decode_display(&edid->display, &raw->display);
249
250	edid->established_timing = raw->established_timing;
251
252	for (i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
253		decode_std_timing(&edid->std_timing[i], &raw->std_timing[i]);
254	}
255
256	decode_detailed_monitor(edid->detailed_monitor, raw->detailed_monitor,
257		edid->version.version == 1 && edid->version.revision >= 1);
258}
259