xref: /haiku/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
1 /*
2  * Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz, mmlr@mlotz.ch
7  *		Alexander von Gluck IV, kallisti5@unixzen.com
8  */
9 
10 
11 #include "FlexibleDisplayInterface.h"
12 
13 #include <stdlib.h>
14 #include <string.h>
15 #include <Debug.h>
16 #include <KernelExport.h>
17 
18 #include "accelerant.h"
19 #include "intel_extreme.h"
20 
21 
22 #undef TRACE
23 #define TRACE_FDI
24 #ifdef TRACE_FDI
25 #   define TRACE(x...) _sPrintf("intel_extreme: " x)
26 #else
27 #   define TRACE(x...)
28 #endif
29 
30 #define ERROR(x...) _sPrintf("intel_extreme: " x)
31 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
32 
33 
34 // #pragma mark - FDITransmitter
35 
36 
37 FDITransmitter::FDITransmitter(pipe_index pipeIndex)
38 	:
39 	fRegisterBase(PCH_FDI_TX_BASE_REGISTER)
40 {
41 	if (pipeIndex == INTEL_PIPE_B)
42 		fRegisterBase += PCH_FDI_TX_PIPE_OFFSET * 1;
43 }
44 
45 
46 FDITransmitter::~FDITransmitter()
47 {
48 }
49 
50 
51 void
52 FDITransmitter::Enable()
53 {
54 	CALLED();
55 	uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL;
56 	uint32 value = read32(targetRegister);
57 
58 	write32(targetRegister, value | FDI_TX_ENABLE);
59 	read32(targetRegister);
60 	spin(150);
61 }
62 
63 
64 void
65 FDITransmitter::Disable()
66 {
67 	CALLED();
68 	uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL;
69 	uint32 value = read32(targetRegister);
70 
71 	write32(targetRegister, value & ~FDI_TX_ENABLE);
72 	read32(targetRegister);
73 	spin(150);
74 }
75 
76 
77 bool
78 FDITransmitter::IsPLLEnabled()
79 {
80 	CALLED();
81 	return (read32(fRegisterBase + PCH_FDI_TX_CONTROL) & FDI_TX_PLL_ENABLED)
82 		!= 0;
83 }
84 
85 
86 void
87 FDITransmitter::EnablePLL()
88 {
89 	CALLED();
90 	uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL;
91 	uint32 value = read32(targetRegister);
92 	if ((value & FDI_TX_PLL_ENABLED) != 0) {
93 		// already enabled, possibly IronLake where it always is
94 		return;
95 	}
96 
97 	write32(targetRegister, value | FDI_TX_PLL_ENABLED);
98 	read32(targetRegister);
99 	spin(100); // warmup 10us + dmi delay 20us, be generous
100 }
101 
102 
103 void
104 FDITransmitter::DisablePLL()
105 {
106 	CALLED();
107 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) {
108 		// on IronLake the FDI PLL is alaways enabled, so no point in trying...
109 		return;
110 	}
111 
112 	uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL;
113 	write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED);
114 	read32(targetRegister);
115 	spin(100);
116 }
117 
118 
119 // #pragma mark - FDIReceiver
120 
121 
122 FDIReceiver::FDIReceiver(pipe_index pipeIndex)
123 	:
124 	fRegisterBase(PCH_FDI_RX_BASE_REGISTER)
125 {
126 	if (pipeIndex == INTEL_PIPE_B)
127 		fRegisterBase += PCH_FDI_RX_PIPE_OFFSET * 1;
128 }
129 
130 
131 FDIReceiver::~FDIReceiver()
132 {
133 }
134 
135 
136 void
137 FDIReceiver::Enable()
138 {
139 	CALLED();
140 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
141 	uint32 value = read32(targetRegister);
142 
143 	write32(targetRegister, value | FDI_RX_ENABLE);
144 	read32(targetRegister);
145 	spin(150);
146 }
147 
148 
149 void
150 FDIReceiver::Disable()
151 {
152 	CALLED();
153 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
154 	uint32 value = read32(targetRegister);
155 
156 	write32(targetRegister, value & ~FDI_RX_ENABLE);
157 	read32(targetRegister);
158 	spin(150);
159 }
160 
161 
162 bool
163 FDIReceiver::IsPLLEnabled()
164 {
165 	CALLED();
166 	return (read32(fRegisterBase + PCH_FDI_RX_CONTROL) & FDI_RX_PLL_ENABLED)
167 		!= 0;
168 }
169 
170 
171 void
172 FDIReceiver::EnablePLL()
173 {
174 	CALLED();
175 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
176 	uint32 value = read32(targetRegister);
177 	if ((value & FDI_RX_PLL_ENABLED) != 0)
178 		return;
179 
180 	write32(targetRegister, value | FDI_RX_PLL_ENABLED);
181 	read32(targetRegister);
182 	spin(200); // warmup 10us + dmi delay 20us, be generous
183 }
184 
185 
186 void
187 FDIReceiver::DisablePLL()
188 {
189 	CALLED();
190 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
191 	write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED);
192 	read32(targetRegister);
193 	spin(100);
194 }
195 
196 
197 void
198 FDIReceiver::SwitchClock(bool toPCDClock)
199 {
200 	CALLED();
201 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
202 	write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK)
203 		| (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW));
204 	read32(targetRegister);
205 	spin(200);
206 }
207 
208 
209 // #pragma mark - FDILink
210 
211 
212 FDILink::FDILink(pipe_index pipeIndex)
213 	:
214 	fTransmitter(pipeIndex),
215 	fReceiver(pipeIndex)
216 {
217 }
218 
219 
220 status_t
221 FDILink::Train(display_mode* target)
222 {
223 	CALLED();
224 
225 	uint32 bitsPerPixel;
226 	switch (target->space) {
227 		case B_RGB32_LITTLE:
228 			bitsPerPixel = 32;
229 			break;
230 		case B_RGB16_LITTLE:
231 			bitsPerPixel = 16;
232 			break;
233 		case B_RGB15_LITTLE:
234 			bitsPerPixel = 15;
235 			break;
236 		case B_CMAP8:
237 		default:
238 			bitsPerPixel = 8;
239 			break;
240 	}
241 
242 	// Khz / 10. ( each output octet encoded as 10 bits.
243 	uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
244 	uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20;
245 
246 	uint32 lanes = bps / (linkBandwidth * 8);
247 
248 	TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes);
249 
250 	// Enable FDI clocks
251 	Receiver().EnablePLL();
252 	Receiver().SwitchClock(true);
253 	Transmitter().EnablePLL();
254 
255 	status_t result = B_ERROR;
256 
257 	// Over IVB supports AutoTraining of FDI
258 	if (gInfo->shared_info->device_type.Generation() >= 7) {
259 		result = _AutoTrain(lanes);
260 		if (result != B_OK) {
261 			ERROR("%s: FDI auto-training fault. Attempting manual train.\n",
262 				__func__);
263 			return _ManualTrain(lanes);
264 		}
265 		return B_OK;
266 	}
267 	return _ManualTrain(lanes);
268 }
269 
270 
271 status_t
272 FDILink::_ManualTrain(uint32 lanes)
273 {
274 	CALLED();
275 
276 	// This needs completed
277 	ERROR("TODO: Manual FDI Link Training\n");
278 
279 	// Enable pipes
280 	Transmitter().Enable();
281 	Receiver().Enable();
282 	return B_OK;
283 }
284 
285 
286 status_t
287 FDILink::_AutoTrain(uint32 lanes)
288 {
289 	CALLED();
290 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
291 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
292 
293 	uint32 buffer = read32(txControl);
294 
295 	// Clear port width selection and set number of lanes
296 	buffer &= ~(7 << 19);
297 	buffer |= (lanes - 1) << 19;
298 
299 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB))
300 		buffer &= ~FDI_LINK_TRAIN_NONE_IVB;
301 	else
302 		buffer &= ~FDI_LINK_TRAIN_NONE;
303 	write32(txControl, buffer);
304 
305 	static const int snb_b_fdi_train_param[] = {
306 		FDI_LINK_TRAIN_400MV_0DB_SNB_B,
307 		FDI_LINK_TRAIN_400MV_6DB_SNB_B,
308 		FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
309 		FDI_LINK_TRAIN_800MV_0DB_SNB_B,
310 	};
311 
312 	bool trained = false;
313 
314 	for (uint32 i = 0; i < (sizeof(snb_b_fdi_train_param)
315 		/ sizeof(snb_b_fdi_train_param[0])); i++) {
316 		for (int j = 0; j < 2; j++) {
317 			buffer = read32(txControl);
318 			buffer |= FDI_AUTO_TRAINING;
319 			buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
320 			buffer |= snb_b_fdi_train_param[i];
321 			write32(txControl, buffer | FDI_TX_ENABLE);
322 
323 			write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
324 
325 			spin(5);
326 
327 			buffer = read32(txControl);
328 			if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
329 				TRACE("%s: FDI auto train complete!\n", __func__);
330 				trained = true;
331 				break;
332 			}
333 
334 			write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
335 			write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
336 			read32(rxControl);
337 
338 			spin(31);
339 		}
340 
341 		// If Trained, we fall out of autotraining
342 		if (trained)
343 			break;
344 	}
345 
346 	if (!trained) {
347 		ERROR("%s: FDI auto train failed!\n", __func__);
348 		return B_ERROR;
349 	}
350 
351 	// Enable ecc on IVB
352 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
353 		write32(rxControl, read32(rxControl)
354 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
355 		read32(rxControl);
356 	}
357 
358 	return B_OK;
359 }
360 
361 
362 FDILink::~FDILink()
363 {
364 }
365