xref: /openbsd-src/sys/dev/pci/drm/apple/dptxep.c (revision 5dd0baa8251145fcf503b060e3a9291086883454)
1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
3 
4 #include <linux/bitfield.h>
5 #include <linux/completion.h>
6 #include <linux/phy/phy.h>
7 #include <linux/delay.h>
8 
9 #include "afk.h"
10 #include "dcp.h"
11 #include "dptxep.h"
12 #include "parser.h"
13 #include "trace.h"
14 
15 struct dcpdptx_connection_cmd {
16 	__le32 unk;
17 	__le32 target;
18 } __attribute__((packed));
19 
20 struct dcpdptx_hotplug_cmd {
21 	u8 _pad0[16];
22 	__le32 unk;
23 } __attribute__((packed));
24 
25 struct dptxport_apcall_link_rate {
26 	__le32 retcode;
27 	u8 _unk0[12];
28 	__le32 link_rate;
29 	u8 _unk1[12];
30 } __attribute__((packed));
31 
32 struct dptxport_apcall_lane_count {
33 	__le32 retcode;
34 	u8 _unk0[12];
35 	__le64 lane_count;
36 	u8 _unk1[8];
37 } __attribute__((packed));
38 
39 struct dptxport_apcall_set_active_lane_count {
40 	__le32 retcode;
41 	u8 _unk0[12];
42 	__le64 lane_count;
43 	u8 _unk1[8];
44 } __packed;
45 
46 struct dptxport_apcall_get_support {
47 	__le32 retcode;
48 	u8 _unk0[12];
49 	__le32 supported;
50 	u8 _unk1[12];
51 } __attribute__((packed));
52 
53 struct dptxport_apcall_max_drive_settings {
54 	__le32 retcode;
55 	u8 _unk0[12];
56 	__le32 max_drive_settings[2];
57 	u8 _unk1[8];
58 };
59 
60 struct dptxport_apcall_drive_settings {
61 	__le32 retcode;
62 	u8 _unk0[12];
63 	__le32 unk1;
64 	__le32 unk2;
65 	__le32 unk3;
66 	__le32 unk4;
67 	__le32 unk5;
68 	__le32 unk6;
69 	__le32 unk7;
70 };
71 
dptxport_validate_connection(struct apple_epic_service * service,u8 core,u8 atc,u8 die)72 int dptxport_validate_connection(struct apple_epic_service *service, u8 core,
73 				 u8 atc, u8 die)
74 {
75 	struct dptx_port *dptx = service->cookie;
76 	struct dcpdptx_connection_cmd cmd, resp;
77 	int ret;
78 	u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
79 		     FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) |
80 		     FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) |
81 		     DCPDPTX_REMOTE_PORT_CONNECTED;
82 
83 	trace_dptxport_validate_connection(dptx, core, atc, die);
84 
85 	cmd.target = cpu_to_le32(target);
86 	cmd.unk = cpu_to_le32(0x100);
87 	ret = afk_service_call(service, 0, 12, &cmd, sizeof(cmd), 40, &resp,
88 			       sizeof(resp), 40);
89 	if (ret)
90 		return ret;
91 
92 	if (le32_to_cpu(resp.target) != target)
93 		return -EINVAL;
94 	if (le32_to_cpu(resp.unk) != 0x100)
95 		return -EINVAL;
96 
97 	return 0;
98 }
99 
dptxport_connect(struct apple_epic_service * service,u8 core,u8 atc,u8 die)100 int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc,
101 		     u8 die)
102 {
103 	struct dptx_port *dptx = service->cookie;
104 	struct dcpdptx_connection_cmd cmd, resp;
105 	u32 unk_field = 0x0; // seen as 0x100 under some conditions
106 	int ret;
107 	u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
108 		     FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) |
109 		     FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) |
110 		     DCPDPTX_REMOTE_PORT_CONNECTED;
111 
112 	trace_dptxport_connect(dptx, core, atc, die);
113 
114 	cmd.target = cpu_to_le32(target);
115 	cmd.unk = cpu_to_le32(unk_field);
116 	ret = afk_service_call(service, 0, 11, &cmd, sizeof(cmd), 24, &resp,
117 			       sizeof(resp), 24);
118 	if (ret)
119 		return ret;
120 
121 	if (le32_to_cpu(resp.target) != target)
122 		return -EINVAL;
123 	if (le32_to_cpu(resp.unk) != unk_field)
124 		dev_notice(service->ep->dcp->dev, "unexpected unk field in reply: 0x%x (0x%x)\n",
125 			  le32_to_cpu(resp.unk), unk_field);
126 
127 	return 0;
128 }
129 
dptxport_request_display(struct apple_epic_service * service)130 int dptxport_request_display(struct apple_epic_service *service)
131 {
132 	return afk_service_call(service, 0, 6, NULL, 0, 16, NULL, 0, 16);
133 }
134 
dptxport_release_display(struct apple_epic_service * service)135 int dptxport_release_display(struct apple_epic_service *service)
136 {
137 	return afk_service_call(service, 0, 7, NULL, 0, 16, NULL, 0, 16);
138 }
139 
dptxport_set_hpd(struct apple_epic_service * service,bool hpd)140 int dptxport_set_hpd(struct apple_epic_service *service, bool hpd)
141 {
142 	struct dcpdptx_hotplug_cmd cmd, resp;
143 	int ret;
144 
145 	memset(&cmd, 0, sizeof(cmd));
146 
147 	if (hpd)
148 		cmd.unk = cpu_to_le32(1);
149 
150 	ret = afk_service_call(service, 8, 8, &cmd, sizeof(cmd), 12, &resp,
151 			       sizeof(resp), 12);
152 	if (ret)
153 		return ret;
154 	if (le32_to_cpu(resp.unk) != 1)
155 		return -EINVAL;
156 	return 0;
157 }
158 
159 static int
dptxport_call_get_max_drive_settings(struct apple_epic_service * service,void * reply_,size_t reply_size)160 dptxport_call_get_max_drive_settings(struct apple_epic_service *service,
161 				     void *reply_, size_t reply_size)
162 {
163 	struct dptxport_apcall_max_drive_settings *reply = reply_;
164 
165 	if (reply_size < sizeof(*reply))
166 		return -EINVAL;
167 
168 	reply->retcode = cpu_to_le32(0);
169 	reply->max_drive_settings[0] = cpu_to_le32(0x3);
170 	reply->max_drive_settings[1] = cpu_to_le32(0x3);
171 
172 	return 0;
173 }
174 
175 static int
dptxport_call_get_drive_settings(struct apple_epic_service * service,const void * request_,size_t request_size,void * reply_,size_t reply_size)176 dptxport_call_get_drive_settings(struct apple_epic_service *service,
177 				     const void *request_, size_t request_size,
178 				     void *reply_, size_t reply_size)
179 {
180 	struct dptx_port *dptx = service->cookie;
181 	const struct dptxport_apcall_drive_settings *request = request_;
182 	struct dptxport_apcall_drive_settings *reply = reply_;
183 
184 	if (reply_size < sizeof(*reply) || request_size < sizeof(*request))
185 		return -EINVAL;
186 
187 	*reply = *request;
188 
189 	/* Clear the rest of the buffer */
190 	memset(reply_ + sizeof(*reply), 0, reply_size - sizeof(*reply));
191 
192 	if (reply->retcode != 4)
193 		dev_err(service->ep->dcp->dev,
194 			"get_drive_settings: unexpected retcode %d\n",
195 			reply->retcode);
196 
197 	reply->retcode = 4; /* Should already be 4? */
198 	reply->unk5 = dptx->drive_settings[0];
199 	reply->unk6 = 0;
200 	reply->unk7 = dptx->drive_settings[1];
201 
202 	return 0;
203 }
204 
205 static int
dptxport_call_set_drive_settings(struct apple_epic_service * service,const void * request_,size_t request_size,void * reply_,size_t reply_size)206 dptxport_call_set_drive_settings(struct apple_epic_service *service,
207 				     const void *request_, size_t request_size,
208 				     void *reply_, size_t reply_size)
209 {
210 	struct dptx_port *dptx = service->cookie;
211 	const struct dptxport_apcall_drive_settings *request = request_;
212 	struct dptxport_apcall_drive_settings *reply = reply_;
213 
214 	if (reply_size < sizeof(*reply) || request_size < sizeof(*request))
215 		return -EINVAL;
216 
217 	*reply = *request;
218 	reply->retcode = cpu_to_le32(0);
219 
220 	dev_info(service->ep->dcp->dev, "set_drive_settings: %d:%d:%d:%d:%d:%d:%d\n",
221 		 request->unk1, request->unk2, request->unk3, request->unk4,
222 		 request->unk5, request->unk6, request->unk7);
223 
224 	dptx->drive_settings[0] = reply->unk5;
225 	dptx->drive_settings[1] = reply->unk7;
226 
227 	return 0;
228 }
229 
dptxport_call_get_max_link_rate(struct apple_epic_service * service,void * reply_,size_t reply_size)230 static int dptxport_call_get_max_link_rate(struct apple_epic_service *service,
231 					   void *reply_, size_t reply_size)
232 {
233 	struct dptxport_apcall_link_rate *reply = reply_;
234 
235 	if (reply_size < sizeof(*reply))
236 		return -EINVAL;
237 
238 	reply->retcode = cpu_to_le32(0);
239 	reply->link_rate = cpu_to_le32(LINK_RATE_HBR3);
240 
241 	return 0;
242 }
243 
dptxport_call_get_max_lane_count(struct apple_epic_service * service,void * reply_,size_t reply_size)244 static int dptxport_call_get_max_lane_count(struct apple_epic_service *service,
245 					   void *reply_, size_t reply_size)
246 {
247 	struct dptxport_apcall_lane_count *reply = reply_;
248 
249 	if (reply_size < sizeof(*reply))
250 		return -EINVAL;
251 
252 	reply->retcode = cpu_to_le32(0);
253 	reply->lane_count = cpu_to_le64(4);
254 
255 	return 0;
256 }
257 
dptxport_call_set_active_lane_count(struct apple_epic_service * service,const void * data,size_t data_size,void * reply_,size_t reply_size)258 static int dptxport_call_set_active_lane_count(struct apple_epic_service *service,
259 					       const void *data, size_t data_size,
260 					       void *reply_, size_t reply_size)
261 {
262 	struct dptx_port *dptx = service->cookie;
263 	const struct dptxport_apcall_set_active_lane_count *request = data;
264 	struct dptxport_apcall_set_active_lane_count *reply = reply_;
265 	int ret = 0;
266 	int retcode = 0;
267 
268 	if (reply_size < sizeof(*reply))
269 		return -1;
270 	if (data_size < sizeof(*request))
271 		return -1;
272 
273 	u64 lane_count = cpu_to_le64(request->lane_count);
274 
275 	switch (lane_count) {
276 	case 0 ... 2:
277 	case 4:
278 		dptx->phy_ops.dp.lanes = lane_count;
279 		dptx->phy_ops.dp.set_lanes = 1;
280 		break;
281 	default:
282 		dev_err(service->ep->dcp->dev, "set_active_lane_count: invalid lane count:%llu\n", lane_count);
283 		retcode = 1;
284 		lane_count = 0;
285 		break;
286 	}
287 
288 	if (dptx->phy_ops.dp.set_lanes) {
289 		if (dptx->atcphy) {
290 			ret = phy_configure(dptx->atcphy, &dptx->phy_ops);
291 			if (ret)
292 				return ret;
293 		}
294 		dptx->phy_ops.dp.set_lanes = 0;
295 	}
296 
297 	dptx->lane_count = lane_count;
298 
299 	reply->retcode = cpu_to_le32(retcode);
300 	reply->lane_count = cpu_to_le64(lane_count);
301 
302 	if (dptx->lane_count > 0)
303 		complete(&dptx->linkcfg_completion);
304 
305 	return ret;
306 }
307 
dptxport_call_get_link_rate(struct apple_epic_service * service,void * reply_,size_t reply_size)308 static int dptxport_call_get_link_rate(struct apple_epic_service *service,
309 				       void *reply_, size_t reply_size)
310 {
311 	struct dptx_port *dptx = service->cookie;
312 	struct dptxport_apcall_link_rate *reply = reply_;
313 
314 	if (reply_size < sizeof(*reply))
315 		return -EINVAL;
316 
317 	reply->retcode = cpu_to_le32(0);
318 	reply->link_rate = cpu_to_le32(dptx->link_rate);
319 
320 	return 0;
321 }
322 
323 static int
dptxport_call_will_change_link_config(struct apple_epic_service * service)324 dptxport_call_will_change_link_config(struct apple_epic_service *service)
325 {
326 	struct dptx_port *dptx = service->cookie;
327 
328 	dptx->phy_ops.dp.set_lanes = 0;
329 	dptx->phy_ops.dp.set_rate = 0;
330 	dptx->phy_ops.dp.set_voltages = 0;
331 
332 	return 0;
333 }
334 
335 static int
dptxport_call_did_change_link_config(struct apple_epic_service * service)336 dptxport_call_did_change_link_config(struct apple_epic_service *service)
337 {
338 	/* assume the link config did change and wait a little bit */
339 	mdelay(10);
340 
341 	return 0;
342 }
343 
dptxport_call_set_link_rate(struct apple_epic_service * service,const void * data,size_t data_size,void * reply_,size_t reply_size)344 static int dptxport_call_set_link_rate(struct apple_epic_service *service,
345 				       const void *data, size_t data_size,
346 				       void *reply_, size_t reply_size)
347 {
348 	struct dptx_port *dptx = service->cookie;
349 	const struct dptxport_apcall_link_rate *request = data;
350 	struct dptxport_apcall_link_rate *reply = reply_;
351 	u32 link_rate, phy_link_rate;
352 	bool phy_set_rate = false;
353 	int ret;
354 
355 	if (reply_size < sizeof(*reply))
356 		return -EINVAL;
357 	if (data_size < sizeof(*request))
358 		return -EINVAL;
359 
360 	link_rate = le32_to_cpu(request->link_rate);
361 	trace_dptxport_call_set_link_rate(dptx, link_rate);
362 
363 	switch (link_rate) {
364 	case LINK_RATE_RBR:
365 		phy_link_rate = 1620;
366 		phy_set_rate = true;
367 		break;
368 	case LINK_RATE_HBR:
369 		phy_link_rate = 2700;
370 		phy_set_rate = true;
371 		break;
372 	case LINK_RATE_HBR2:
373 		phy_link_rate = 5400;
374 		phy_set_rate = true;
375 		break;
376 	case LINK_RATE_HBR3:
377 		phy_link_rate = 8100;
378 		phy_set_rate = true;
379 		break;
380 	case 0:
381 		phy_link_rate = 0;
382 		phy_set_rate = true;
383 		break;
384 	default:
385 		dev_err(service->ep->dcp->dev,
386 			"DPTXPort: Unsupported link rate 0x%x requested\n",
387 			link_rate);
388 		link_rate = 0;
389 		phy_set_rate = false;
390 		break;
391 	}
392 
393 	if (phy_set_rate) {
394 		dptx->phy_ops.dp.link_rate = phy_link_rate;
395 		dptx->phy_ops.dp.set_rate = 1;
396 
397 		if (dptx->atcphy) {
398 			ret = phy_configure(dptx->atcphy, &dptx->phy_ops);
399 			if (ret)
400 				return ret;
401 		}
402 
403 		//if (dptx->phy_ops.dp.set_rate)
404 		dptx->link_rate = dptx->pending_link_rate = link_rate;
405 
406 	}
407 
408 	//dptx->pending_link_rate = link_rate;
409 	reply->retcode = cpu_to_le32(0);
410 	reply->link_rate = cpu_to_le32(link_rate);
411 
412 	return 0;
413 }
414 
dptxport_call_get_supports_hpd(struct apple_epic_service * service,void * reply_,size_t reply_size)415 static int dptxport_call_get_supports_hpd(struct apple_epic_service *service,
416 					  void *reply_, size_t reply_size)
417 {
418 	struct dptxport_apcall_get_support *reply = reply_;
419 
420 	if (reply_size < sizeof(*reply))
421 		return -EINVAL;
422 
423 	reply->retcode = cpu_to_le32(0);
424 	reply->supported = cpu_to_le32(0);
425 	return 0;
426 }
427 
428 static int
dptxport_call_get_supports_downspread(struct apple_epic_service * service,void * reply_,size_t reply_size)429 dptxport_call_get_supports_downspread(struct apple_epic_service *service,
430 				      void *reply_, size_t reply_size)
431 {
432 	struct dptxport_apcall_get_support *reply = reply_;
433 
434 	if (reply_size < sizeof(*reply))
435 		return -EINVAL;
436 
437 	reply->retcode = cpu_to_le32(0);
438 	reply->supported = cpu_to_le32(0);
439 	return 0;
440 }
441 
442 static int
dptxport_call_activate(struct apple_epic_service * service,const void * data,size_t data_size,void * reply,size_t reply_size)443 dptxport_call_activate(struct apple_epic_service *service,
444 		       const void *data, size_t data_size,
445 		       void *reply, size_t reply_size)
446 {
447 	struct dptx_port *dptx = service->cookie;
448 	const struct apple_dcp *dcp = service->ep->dcp;
449 
450 	// TODO: hack, use phy_set_mode to select the correct DCP(EXT) input
451 	phy_set_mode_ext(dptx->atcphy, PHY_MODE_DP, dcp->index);
452 
453 	memcpy(reply, data, min(reply_size, data_size));
454 	if (reply_size >= 4)
455 		memset(reply, 0, 4);
456 
457 	return 0;
458 }
459 
460 static int
dptxport_call_deactivate(struct apple_epic_service * service,const void * data,size_t data_size,void * reply,size_t reply_size)461 dptxport_call_deactivate(struct apple_epic_service *service,
462 		       const void *data, size_t data_size,
463 		       void *reply, size_t reply_size)
464 {
465 	struct dptx_port *dptx = service->cookie;
466 
467 	/* deactivate phy */
468 	phy_set_mode_ext(dptx->atcphy, PHY_MODE_INVALID, 0);
469 
470 	memcpy(reply, data, min(reply_size, data_size));
471 	if (reply_size >= 4)
472 		memset(reply, 0, 4);
473 
474 	return 0;
475 }
476 
dptxport_call(struct apple_epic_service * service,u32 idx,const void * data,size_t data_size,void * reply,size_t reply_size)477 static int dptxport_call(struct apple_epic_service *service, u32 idx,
478 			 const void *data, size_t data_size, void *reply,
479 			 size_t reply_size)
480 {
481 	struct dptx_port *dptx = service->cookie;
482 	trace_dptxport_apcall(dptx, idx, data_size);
483 
484 	switch (idx) {
485 	case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG:
486 		return dptxport_call_will_change_link_config(service);
487 	case DPTX_APCALL_DID_CHANGE_LINK_CONFIG:
488 		return dptxport_call_did_change_link_config(service);
489 	case DPTX_APCALL_GET_MAX_LINK_RATE:
490 		return dptxport_call_get_max_link_rate(service, reply,
491 						       reply_size);
492 	case DPTX_APCALL_GET_LINK_RATE:
493 		return dptxport_call_get_link_rate(service, reply, reply_size);
494 	case DPTX_APCALL_SET_LINK_RATE:
495 		return dptxport_call_set_link_rate(service, data, data_size,
496 						   reply, reply_size);
497 	case DPTX_APCALL_GET_MAX_LANE_COUNT:
498 		return dptxport_call_get_max_lane_count(service, reply, reply_size);
499         case DPTX_APCALL_SET_ACTIVE_LANE_COUNT:
500 		return dptxport_call_set_active_lane_count(service, data, data_size,
501 							   reply, reply_size);
502 	case DPTX_APCALL_GET_SUPPORTS_HPD:
503 		return dptxport_call_get_supports_hpd(service, reply,
504 						      reply_size);
505 	case DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD:
506 		return dptxport_call_get_supports_downspread(service, reply,
507 							     reply_size);
508 	case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS:
509 		return dptxport_call_get_max_drive_settings(service, reply,
510 							    reply_size);
511 	case DPTX_APCALL_GET_DRIVE_SETTINGS:
512 		return dptxport_call_get_drive_settings(service, data, data_size,
513 							reply, reply_size);
514 	case DPTX_APCALL_SET_DRIVE_SETTINGS:
515 		return dptxport_call_set_drive_settings(service, data, data_size,
516 							reply, reply_size);
517         case DPTX_APCALL_ACTIVATE:
518 		return dptxport_call_activate(service, data, data_size,
519 					      reply, reply_size);
520 	case DPTX_APCALL_DEACTIVATE:
521 		return dptxport_call_deactivate(service, data, data_size,
522 						reply, reply_size);
523 	default:
524 		/* just try to ACK and hope for the best... */
525 		dev_info(service->ep->dcp->dev, "DPTXPort: acking unhandled call %u\n",
526 			idx);
527 		memcpy(reply, data, min(reply_size, data_size));
528 		if (reply_size >= 4)
529 			memset(reply, 0, 4);
530 		return 0;
531 	}
532 }
533 
dptxport_init(struct apple_epic_service * service,const char * name,const char * class,s64 unit)534 static void dptxport_init(struct apple_epic_service *service, const char *name,
535 			  const char *class, s64 unit)
536 {
537 
538 	if (strcmp(name, "dcpdptx-port-epic"))
539 		return;
540 	if (strcmp(class, "AppleDCPDPTXRemotePort"))
541 		return;
542 
543 	trace_dptxport_init(service->ep->dcp, unit);
544 
545 	switch (unit) {
546 	case 0:
547 	case 1:
548 		if (service->ep->dcp->dptxport[unit].enabled) {
549 			dev_err(service->ep->dcp->dev,
550 				"DPTXPort: unit %lld already exists\n", unit);
551 			return;
552 		}
553 		service->ep->dcp->dptxport[unit].unit = unit;
554 		service->ep->dcp->dptxport[unit].service = service;
555 		service->ep->dcp->dptxport[unit].enabled = true;
556 		service->cookie = (void *)&service->ep->dcp->dptxport[unit];
557 		complete(&service->ep->dcp->dptxport[unit].enable_completion);
558 		break;
559 	default:
560 		dev_err(service->ep->dcp->dev, "DPTXPort: invalid unit %lld\n",
561 			unit);
562 	}
563 }
564 
565 static const struct apple_epic_service_ops dptxep_ops[] = {
566 	{
567 		.name = "AppleDCPDPTXRemotePort",
568 		.init = dptxport_init,
569 		.call = dptxport_call,
570 	},
571 	{}
572 };
573 
dptxep_init(struct apple_dcp * dcp)574 int dptxep_init(struct apple_dcp *dcp)
575 {
576 	int ret;
577 	u32 port;
578 	unsigned long timeout = msecs_to_jiffies(1000);
579 
580 	init_completion(&dcp->dptxport[0].enable_completion);
581 	init_completion(&dcp->dptxport[1].enable_completion);
582 	init_completion(&dcp->dptxport[0].linkcfg_completion);
583 	init_completion(&dcp->dptxport[1].linkcfg_completion);
584 
585 	dcp->dptxep = afk_init(dcp, DPTX_ENDPOINT, dptxep_ops);
586 	if (IS_ERR(dcp->dptxep))
587 		return PTR_ERR(dcp->dptxep);
588 
589 	ret = afk_start(dcp->dptxep);
590 	if (ret)
591 		return ret;
592 
593 	for (port = 0; port < dcp->hw.num_dptx_ports; port++) {
594 		ret = wait_for_completion_timeout(&dcp->dptxport[port].enable_completion,
595 						timeout);
596 		if (!ret)
597 			return -ETIMEDOUT;
598 		else if (ret < 0)
599 			return ret;
600 		timeout = ret;
601 	}
602 
603 	return 0;
604 }
605