xref: /haiku/src/add-ons/accelerants/common/ddc.c (revision fc1ca2da5cfcb00ffdf791606d5ae97fdd58a638)
1 /*
2  * Copyright 2003, Thomas Kurschel. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 /*!	DDC communication */
7 
8 
9 #include "ddc_int.h"
10 #include "ddc.h"
11 #include "i2c.h"
12 
13 #include <KernelExport.h>
14 #include <OS.h>
15 
16 #include <stdlib.h>
17 
18 
19 #define READ_RETRIES 4		// number of retries to read ddc data
20 
21 
22 #define TRACE_DDC
23 #ifdef TRACE_DDC
24 extern void _sPrintf(const char* format, ...);
25 #	define TRACE(x...) _sPrintf("DDC: " x)
26 #else
27 #	define TRACE(x...) ;
28 #endif
29 
30 
31 
32 //! Verify checksum of DDC data.
33 static status_t
34 verify_checksum(const uint8 *data, size_t len)
35 {
36 	uint32 index;
37 	uint8 sum = 0;
38 	uint8 allOr = 0;
39 
40 	for (index = 0; index < len; ++index, ++data) {
41 		sum += *data;
42 		allOr |= *data;
43 	}
44 
45 	if (allOr == 0) {
46 		TRACE("verify_checksum() DDC information contains zeros only\n");
47 		return B_ERROR;
48 	}
49 
50 	if (sum != 0) {
51 		TRACE("verify_checksum() Checksum error in DDC information\n");
52 		return B_IO_ERROR;
53 	}
54 
55 	return B_OK;
56 }
57 
58 
59 //!	Read ddc2 data from monitor
60 static status_t
61 ddc2_read(const i2c_bus *bus, int start, uint8 *buffer, size_t length)
62 {
63 	status_t status = B_OK;
64 	uint8 writeBuffer[2];
65 	int i;
66 
67 	writeBuffer[0] = start & 0xff;
68 	writeBuffer[1] = (start >> 8) & 0xff;
69 
70 	for (i = 0; i < READ_RETRIES; ++i) {
71 		status = i2c_send_receive(bus, 0xa0, writeBuffer,
72 			start < 0x100 ? 1 : 2, buffer, length);
73 
74 		if (status != B_OK)
75 			TRACE("ddc2_read(): DDC information read failure\n");
76 
77 		if (status == B_OK) {
78 			status = verify_checksum(buffer, length);
79 			if (status == B_OK)
80 				break;
81 
82 			dprintf("DDC checksum incorrect!\n");
83 		}
84 	}
85 
86 	return status;
87 }
88 
89 
90 /*!
91 	Reading VDIF has not been tested.
92 	it seems that almost noone supports VDIF which makes testing hard,
93 	but what's the point anyway?
94 */
95 #if 0
96 static status_t
97 ddc2_read_vdif(const i2c_bus *bus, int start,
98 	void **vdif, size_t *vdif_len)
99 {
100 	status_t res;
101 	uint8 *data, *cur_data;
102 	int i;
103 	uint8 buffer[64];
104 
105 	*vdif = NULL;
106 	*vdif_len = 0;
107 
108 	res = ddc2_read(bus, start, buffer, 64);
109 	SHOW_INFO(2, "%x", buffer[0]);
110 	if (res != B_OK || buffer[0] == 0)
111 		return B_OK;
112 
113 	// each block is 63 bytes plus 1 checksum long
114 	// we strip the checksum but store data directly into
115 	// buffer, so we need an extra byte for checksum of the last block
116 	data = malloc(buffer[0] * 63 + 1);
117 	if (data == NULL)
118 		return B_NO_MEMORY;
119 
120 	cur_data = data;
121 	for (i = 0; i < buffer[0]; ++i) {
122 		ddc2_read(bus, start + i * 64, cur_data, 64);
123 		// strip checksum byte
124 		cur_data += 63;
125 	}
126 
127 	*vdif_len = buffer[0] * 63;
128 	*vdif = data;
129 	return B_OK;
130 }
131 #endif
132 
133 
134 //	#pragma mark -
135 
136 
137 void
138 ddc2_init_timing(i2c_bus *bus)
139 {
140 	i2c_get100k_timing(&bus->timing);
141 
142 	// VESA standard
143 	bus->timing.start_timeout = 550;
144 	bus->timing.byte_timeout = 2200;
145 	bus->timing.bit_timeout = 40;
146 	bus->timing.ack_start_timeout = 40;
147 	bus->timing.ack_timeout = 40;
148 }
149 
150 
151 //! Read EDID and VDIF from monitor via ddc2
152 status_t
153 ddc2_read_edid1(const i2c_bus *bus, edid1_info *edid,
154 	void **vdif, size_t *vdifLength)
155 {
156 	edid1_raw raw;
157 	status_t status = ddc2_read(bus, 0, (uint8 *)&raw, sizeof(raw));
158 	if (status != B_OK)
159 		return status;
160 
161 	if (raw.version.version != 1 || raw.version.revision > 4) {
162 		TRACE("ddc2_read_edid1() EDID version or revision out of range\n");
163 		return B_ERROR;
164 	}
165 
166 	edid_decode(edid, &raw);
167 
168 	if (vdif != NULL)
169 		*vdif = NULL;
170 	if (vdifLength != NULL)
171 		*vdifLength = 0;
172 
173 	// skip vdif as long as it's not tested
174 #if 0
175 	status = ddc2_read_vdif(bus, sizeof(raw) * (edid->num_sections + 1),
176 		vdif, vdifLength);
177 	if (status != B_OK)
178 		return status;
179 #endif
180 
181 	return B_OK;
182 }
183