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