xref: /haiku/src/add-ons/media/media-add-ons/radeon/I2CPort.cpp (revision c90684742e7361651849be4116d0e5de3a817194)
1 /******************************************************************************
2 /
3 /	File:			I2C.cpp
4 /
5 /	Description:	ATI Radeon I2C Serial Bus interface.
6 /
7 /	Copyright 2001, Carlos Hasan
8 /
9 *******************************************************************************/
10 
11 #include <Debug.h>
12 #include "I2CPort.h"
13 
14 CI2CPort::CI2CPort(CRadeon & radeon, int rate)
15 	:	fRadeon(radeon),
16 		fNfactor(0),
17 		fMfactor(0),
18 		fTimeLimit(0),
19 		si(NULL)
20 {
21 
22 	PRINT(("CI2CPort::CI2CPort()\n"));
23 
24 	if( fRadeon.InitCheck() == B_OK ) {
25 		int refFreq, refDiv, minFreq, maxFreq, xclock;
26 		double n;
27 
28 		fRadeon.GetPLLParameters(refFreq, refDiv, minFreq, maxFreq, xclock);
29 		si = fRadeon.GetSharedInfo();
30 
31 		if ( si->asic == rt_rv200 ) {
32 			n = (xclock * 40000.0) / (1.0 * rate);
33 		} else {
34 			n = (xclock * 10000.0) / (4.0 * rate);
35 		}
36 
37 		for (fNfactor = 1; fNfactor < 255; fNfactor++) {
38 			if (fNfactor * (fNfactor - 1) > n)
39 				break;
40 		}
41 		fMfactor = fNfactor - 1;
42 		fTimeLimit = 2 * fNfactor;
43 
44 		// enable I2C bus
45 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0x00ff0000,
46 			C_RADEON_I2C_SEL | C_RADEON_I2C_EN);
47 
48 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_0, 0x000000ff,
49 			C_RADEON_I2C_DONE | C_RADEON_I2C_NACK |
50 			C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST |
51 			C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_DRIVE_SEL);
52 
53 #if DEBUG
54 		PRINT(("CI2CPort::CI2CPort() - I2C devices found at ports: "));
55 		for (int address = 0x80; address <= 0xff; address += 0x02) {
56 			if (Probe(address))
57 				PRINT(("0x%02x ", address));
58 		}
59 		PRINT(("\n"));
60 #endif
61 	}
62 }
63 
64 CI2CPort::~CI2CPort()
65 {
66 	PRINT(("CI2CPort::~CI2CPort()\n"));
67 	if( fRadeon.InitCheck() == B_OK ) {
68 		// disable I2C bus
69 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_1, 0);
70 	}
71 }
72 
73 status_t CI2CPort::InitCheck() const
74 {
75 	if (fRadeon.InitCheck() != B_OK)
76 		return B_ERROR;
77 
78 	if ( si == NULL )
79 		return B_ERROR;
80 
81 	if ( si->has_no_i2c ) {
82 		PRINT(("This Chips I2C is BLACKLISTED!"));
83 		return B_ERROR;
84 	}
85 	return B_OK;
86 }
87 
88 CRadeon & CI2CPort::Radeon() const
89 {
90 	return fRadeon;
91 }
92 
93 bool CI2CPort::Probe(int address)
94 {
95 	char buffer[1];
96 
97 	return Read(address, buffer, sizeof(buffer));
98 }
99 
100 bool CI2CPort::Write(int address, const char * buffer, int length)
101 {
102 	if (Send(address, buffer, length, true, true) == length)
103 		return true;
104 	return false;
105 }
106 
107 bool CI2CPort::Read(int address, char * buffer, int length)
108 {
109 	if (Receive(address, buffer, length, true, true) == length)
110 		return true;
111 	return false;
112 }
113 
114 bool CI2CPort::Write(int address, const char * buffer, int length, char * result, int reslen)
115 {
116 	if (Send(address, buffer, length, true, false) == length)
117 		if (Receive(address, result, reslen, true, true) == reslen)
118 			return true;
119 	return false;
120 }
121 
122 int CI2CPort::Register(int address, int index)
123 {
124 	char value = index;
125 
126 	if (Send(address, &value, sizeof(value), true, false) == sizeof(value)) {
127 		if (Receive(address, &value, sizeof(value), true, true) == sizeof(value))
128 			return value & 0xff;
129 	}
130 	PRINT(("CI2CPort::Register() - error\n"));
131 	return -1;
132 }
133 
134 void CI2CPort::SetRegister(int address, int index, int value)
135 {
136 	char buffer[2];
137 
138 	buffer[0] = index;
139 	buffer[1] = value;
140 
141 	if (Send(address, buffer, sizeof(buffer), true, true) != sizeof(buffer))
142 		PRINT(("CI2CPort::SetRegister() - error\n"));
143 }
144 
145 int CI2CPort::Send(int address, const char * buffer, int length, bool start, bool stop)
146 {
147 	//fRadeon.WaitForFifo(4 + length);
148 
149 	// clear status bit
150 	fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
151 		C_RADEON_I2C_DONE |	C_RADEON_I2C_NACK |
152 		C_RADEON_I2C_HALT |	C_RADEON_I2C_SOFT_RST);
153 
154 	// write address
155 	fRadeon.SetRegister(C_RADEON_I2C_DATA, address & ~(1));
156 
157 	// write data
158 	for (int offset = 0; offset < length; offset++)
159 		fRadeon.SetRegister(C_RADEON_I2C_DATA, buffer[offset]);
160 
161 	//
162 	if (si->asic >= rt_r200) {
163 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
164 			(fTimeLimit << 24) | length |
165 			C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x010);
166 	} else {
167 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
168 			(fTimeLimit << 24) | length |
169 			C_RADEON_I2C_EN | C_RADEON_I2C_SEL | 0x100);
170 	}
171 
172 	fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
173 		(fNfactor << 24) | (fMfactor << 16) |
174 		C_RADEON_I2C_GO | C_RADEON_I2C_DRIVE_EN |
175 		(start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0));
176 
177 	for (int wait = 0; wait < 100; wait++) {
178 		if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0)
179 			break;
180 	}
181 
182 	if (WaitAck() != C_RADEON_I2C_DONE) {
183 		Stop();
184 		return 0;
185 	}
186 
187 	return length;
188 }
189 
190 int CI2CPort::Receive(int address, char * buffer, int length, bool start, bool stop)
191 {
192 	//fRadeon.WaitForFifo(length + 4);
193 
194 	fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
195 		C_RADEON_I2C_DONE | C_RADEON_I2C_NACK |
196 		C_RADEON_I2C_HALT | C_RADEON_I2C_SOFT_RST);
197 
198 	fRadeon.SetRegister(C_RADEON_I2C_DATA, address | 0x00000001);
199 
200 	if (si->asic >= rt_r200) {
201 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
202 			(fTimeLimit << 24) | C_RADEON_I2C_EN | C_RADEON_I2C_SEL |
203 			length | 0x010);
204 	} else {
205 		fRadeon.SetRegister(C_RADEON_I2C_CNTL_1,
206 			(fTimeLimit << 24) | C_RADEON_I2C_EN | C_RADEON_I2C_SEL |
207 			length | 0x100);
208 	}
209 
210 	fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
211 		(fNfactor << 24) | (fMfactor << 16) | C_RADEON_I2C_GO |
212 		(start ? C_RADEON_I2C_START : 0) | (stop ? C_RADEON_I2C_STOP : 0) |
213 		C_RADEON_I2C_DRIVE_EN | C_RADEON_I2C_RECEIVE);
214 
215 
216 	for (int wait = 0; wait < 100; wait++) {
217 		if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0)
218 			break;
219 	}
220 
221 	if (WaitAck() != C_RADEON_I2C_DONE)
222 		return 0;
223 
224 	snooze(1000);
225 
226 	for (int offset = 0; offset < length; offset++) {
227 		//fRadeon.WaitForFifo(1);
228 		buffer[offset] = fRadeon.Register(C_RADEON_I2C_DATA) & 0xff;
229 	}
230 
231 	return length;
232 }
233 
234 int CI2CPort::WaitAck()
235 {
236 	for (int wait = 0; wait < 100; wait++) {
237 		int control = fRadeon.Register(C_RADEON_I2C_CNTL_0);
238 		if ((control & C_RADEON_I2C_HALT) != 0)
239 			return C_RADEON_I2C_HALT;
240 		if ((control & C_RADEON_I2C_NACK) != 0)
241 			return C_RADEON_I2C_NACK;
242 		if ((control & C_RADEON_I2C_DONE) != 0)
243 			return C_RADEON_I2C_DONE;
244 		snooze(1000);
245 	}
246 	PRINT(("CI2CPort::WaitAck() - Time out!\n"));
247 	return C_RADEON_I2C_HALT;
248 }
249 
250 void CI2CPort::Stop()
251 {
252 	// reset status flags
253 	fRadeon.SetRegister(C_RADEON_I2C_CNTL_0,
254 		C_RADEON_I2C_DONE | C_RADEON_I2C_NACK | C_RADEON_I2C_HALT, 0);
255 
256 	// issue abort call
257 	fRadeon.SetRegister(C_RADEON_I2C_CNTL_0_PLUS1,
258 		C_RADEON_I2C_ABORT | C_RADEON_I2C_GO, C_RADEON_I2C_ABORT | C_RADEON_I2C_GO);
259 
260 	// wait GO bit to go low
261 	for (int wait = 0; wait < 100; wait++) {
262 		if ((fRadeon.Register(C_RADEON_I2C_CNTL_0) & C_RADEON_I2C_GO) == 0)
263 		snooze(1000);
264 	}
265 	PRINT(("CI2CPort::Stop() - Time out!\n"));
266 }
267