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