xref: /haiku/src/add-ons/accelerants/matrox/engine/mga_i2c.c (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
1 /*
2  * i2c interface for the G400 MAVEN under BeOS
3  *
4  * Provides I2CR,I2CW - functions to parallel DACW,DACR
5  * Bus should be run at max. 100kHz: see original Philips I2C specification
6  *
7  * Much help was provided by observing the Linux i2c code,
8  * so thanks go to: Gerd Knorr
9  *
10  * Other authors:
11  * Mark Watson 6/2000,
12  * Rudolf Cornelissen 12/2002
13  */
14 
15 #define MODULE_BIT 0x00004000
16 
17 #include "mga_std.h"
18 
19 /*which device on the bus is the MAVEN?*/
20 #define MAVEN_WRITE (0x1B<<1)
21 #define MAVEN_READ ((0x1B<<1)|1)
22 
23 #define I2C_CLOCK 0x20
24 #define I2C_DATA 0x10
25 
26 /* MGA-TVO I2C for G200, G400 */
27 #define I2C_CLOCK 0x20
28 #define I2C_DATA 0x10
29 /* primary head DDC for Mystique(?), G100, G200, G400 */
30 #define DDC1_CLK	0x08
31 #define DDC1_DATA	0x02
32 /* primary head DDC for Millennium, Millennium II */
33 #define DDC1B_CLK	0x10
34 #define DDC1B_DATA	0x04
35 /* secondary head DDC for G400, G450 and G550 */
36 #define DDC2_CLK	0x04
37 #define DDC2_DATA	0x01
38 
39 status_t i2c_sec_tv_adapter()
40 {
41 	status_t result = B_ERROR;
42 
43 	/* The secondary DDC channel only exist on dualhead cards */
44 	if (!si->ps.secondary_head) return result;
45 
46 	/* make sure the output lines will be active-low when enabled
47 	 * (they will be pulled 'passive-high' when disabled) */
48 	DXIW(GENIODATA,0x00);
49 	/* send out B_STOP condition on secondary head DDC channel and use it to
50 	 * check for 'shortcut', indicating the Matrox VGA->TV adapter is connected */
51 
52 	/* make sure SDA is low */
53 	DXIW(GENIOCTRL, (DXIR(GENIOCTRL) | DDC2_DATA));
54 	snooze(2);
55 	/* make sure SCL should be high */
56 	DXIW(GENIOCTRL, (DXIR(GENIOCTRL) & ~DDC2_CLK));
57 	snooze(2);
58 	/* if SCL is low then the bus is blocked by a TV adapter */
59 	if (!(DXIR(GENIODATA) & DDC2_CLK)) result = B_OK;
60 	snooze(5);
61 	/* set SDA while SCL should be set (generates actual bus-stop condition) */
62 	DXIW(GENIOCTRL, (DXIR(GENIOCTRL) & ~DDC2_DATA));
63 	snooze(5);
64 
65 	return result;
66 }
67 
68 /*-----------------------------
69  *low level hardware access
70  */
71 #define I2C_DELAY 2
72 #define I2C_TIMEOUT 100
73 static int i2c_set_lines(int clock,int data)
74 {
75 	int count=0;
76 	int program;
77 	int required;
78 
79 	/*work out which bits to zero*/
80 	program =
81 		(clock ? 0 : I2C_CLOCK)|
82 		(data ? 0 : I2C_DATA);
83 
84 	/*what value do I require on data lines*/
85 	required =
86 		(clock ? I2C_CLOCK : 0);
87 
88 	/*set the bits to zero*/
89 	DXIW(GENIOCTRL,program); /*drive these bits*/
90 	DXIW(GENIODATA,0x00);    /*to zero*/
91 
92 	/*wait a bit*/
93 	delay(I2C_DELAY);
94 
95 	/*loop until the clock is as required*/
96 	while ((DXIR(GENIODATA)&I2C_CLOCK)!=required)
97 	{
98 		delay(I2C_DELAY);
99 		count++;
100 		if (count>I2C_TIMEOUT)
101 		{
102 			LOG(8,("I2C: Timeout on set lines - clock:%d data:%d actual:%x\n",clock,data,DXIR(GENIODATA)));
103 			return -1;
104 		}
105 	}
106 
107 	return 0;
108 }
109 
110 static int i2c_get_data()
111 {
112 	int data;
113 	int clock;
114 	int count=0;
115 
116 	do
117 	{
118 		/*read the data and clock lines*/
119 		data = DXIR(GENIODATA);
120 		clock = (data&I2C_CLOCK) ? 1 : 0;
121 		data = (data&I2C_DATA) ? 1 : 0;
122 
123 		/*manage timeout*/
124 		count++;
125 		if (count>I2C_TIMEOUT)
126 		{
127 			return -1;
128 		}
129 
130 		/*wait a bit, so not hammering bus*/
131 		delay(I2C_DELAY);
132 
133 	}while (!clock); /*wait for high clock*/
134 
135 	return data;
136 }
137 
138 
139 /*-----------------------
140  *Standard I2C operations
141  */
142 static void i2c_start()
143 {
144 	int error=0;
145 
146 	error+= i2c_set_lines(0,1);
147 	error+= i2c_set_lines(1,1);
148 	error+= i2c_set_lines(1,0);
149 	error+= i2c_set_lines(0,0);
150 
151 	if (error)
152 	{
153 		LOG(8,("I2C: start - %d\n",error));
154 	}
155 }
156 
157 static void i2c_stop()
158 {
159 	int error=0;
160 
161 	error+= i2c_set_lines(0,0);
162 	error+= i2c_set_lines(1,0);
163 	error+= i2c_set_lines(1,1);
164 	error+= i2c_set_lines(0,1);
165 
166 	if (error)
167 	{
168 		LOG(8,("I2C: stop - %d\n",error));
169 	}
170 }
171 
172 static void i2c_high()
173 {
174 	int error=0;
175 
176 	error+= i2c_set_lines(0,1);
177 	error+= i2c_set_lines(1,1);
178 	error+= i2c_set_lines(0,1);
179 
180 	if (error)
181 	{
182 		LOG(8,("I2C: high - %d\n",error));
183 	}
184 }
185 
186 static void i2c_low()
187 {
188 	int error=0;
189 
190 	error+= i2c_set_lines(0,0);
191 	error+= i2c_set_lines(1,0);
192 	error+= i2c_set_lines(0,0);
193 
194 	if (error)
195 	{
196 		LOG(8,("I2C: low - %d\n",error));
197 	}
198 }
199 
200 static int i2c_get_ack()
201 {
202 	int error=0;
203 	int ack;
204 
205 	error+= i2c_set_lines(0,1);
206 	error+= i2c_set_lines(1,1);
207 	ack = i2c_get_data();
208 	error+= i2c_set_lines(0,1);
209 
210 	if (error)
211 	{
212 		LOG(8,("I2C: get_ack - %d value:%x\n",error,ack));
213 	}
214 
215 	return ack;
216 }
217 
218 static void i2c_send_ack()
219 {
220 	int error=0;
221 
222 	error+= i2c_set_lines(0,0);
223 	error+= i2c_set_lines(1,0);
224 	error+= i2c_set_lines(0,0);
225 
226 	if (error)
227 	{
228 		LOG(8,("I2C: send_ack - %d\n",error));
229 	}
230 }
231 
232 /*------------------------------
233  *use above functions to send and receive bytes
234  */
235 
236 static int i2c_sendbyte(unsigned char data)
237 {
238 	int i;
239 
240 	for (i=7; i>=0; i--)
241 	{
242 		if (data&(1<<i))
243 		{
244 			i2c_high();
245 		}
246 		else
247 		{
248 			i2c_low();
249 		}
250 	}
251 
252 	return i2c_get_ack();
253 }
254 
255 static unsigned char i2c_readbyte(int ack_required)
256 {
257 	int i;
258 	unsigned char data=0;
259 
260 	/*read data*/
261 	i2c_set_lines(0,1);
262 	for (i=7; i>=0; i--)
263 	{
264 		i2c_set_lines(1,1);
265 		if (i2c_get_data()==1)
266 			data |= (1<<i);
267 		i2c_set_lines(0,1);
268 	}
269 
270 	/*send acknowledge*/
271 	if (ack_required) i2c_send_ack();
272 
273 	return data;
274 }
275 
276 /*-------------------------------------------
277  *PUBLIC functions
278  */
279 int i2c_maven_read(unsigned char address)
280 {
281 	int error=0;
282 	int data;
283 
284 	i2c_start();
285 	{
286 		error+=i2c_sendbyte(MAVEN_READ);
287 		error+=i2c_sendbyte(address);
288 		data = i2c_readbyte(0);
289 	}
290 	i2c_stop();
291 	if (error>0) LOG(8,("I2C: MAVR ERROR - %x\n",error));
292 	return data;
293 }
294 
295 void i2c_maven_write(unsigned char address, unsigned char data)
296 {
297 	int error=0;
298 
299 	i2c_start();
300 	{
301 		error+=i2c_sendbyte(MAVEN_WRITE);
302 		error+=i2c_sendbyte(address);
303 		error+=i2c_sendbyte(data);
304 	}
305 	i2c_stop();
306 	if (error>0) LOG(8,("I2C: MAVW ERROR - %x\n",error));
307 }
308 
309 status_t i2c_init(void)
310 {
311 	/*init g400 i2c*/
312 	DXIW(GENIODATA,0x00); /*to zero*/
313 	DXIW(GENIOCTRL,0x30); /*drive clock and data*/
314 	DXIW(GENIOCTRL,0x00); /*stop driving*/
315 
316 	return B_OK;
317 }
318 
319 status_t i2c_maven_probe(void)
320 {
321 	int ack;
322 
323 	/*scan the bus for the MAVEN*/
324 	i2c_start();
325 	{
326 		ack = i2c_sendbyte(MAVEN_READ);
327 	}
328 	i2c_stop();
329 	if (ack==0)
330 	{
331 		return B_OK;
332 	}
333 	else
334 	{
335 		return B_ERROR;
336 	}
337 }
338