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 15struct dcpdptx_connection_cmd { 16 __le32 unk; 17 __le32 target; 18} __attribute__((packed)); 19 20struct dcpdptx_hotplug_cmd { 21 u8 _pad0[16]; 22 __le32 unk; 23} __attribute__((packed)); 24 25struct dptxport_apcall_link_rate { 26 __le32 retcode; 27 u8 _unk0[12]; 28 __le32 link_rate; 29 u8 _unk1[12]; 30} __attribute__((packed)); 31 32struct dptxport_apcall_lane_count { 33 __le32 retcode; 34 u8 _unk0[12]; 35 __le64 lane_count; 36 u8 _unk1[8]; 37} __attribute__((packed)); 38 39struct dptxport_apcall_set_active_lane_count { 40 __le32 retcode; 41 u8 _unk0[12]; 42 __le64 lane_count; 43 u8 _unk1[8]; 44} __packed; 45 46struct dptxport_apcall_get_support { 47 __le32 retcode; 48 u8 _unk0[12]; 49 __le32 supported; 50 u8 _unk1[12]; 51} __attribute__((packed)); 52 53struct dptxport_apcall_max_drive_settings { 54 __le32 retcode; 55 u8 _unk0[12]; 56 __le32 max_drive_settings[2]; 57 u8 _unk1[8]; 58}; 59 60struct 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 72int 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 100int 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 130int 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 135int 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 140int 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 159static int 160dptxport_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 175static int 176dptxport_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 205static int 206dptxport_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 230static 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 244static 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 258static 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 308static 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 323static int 324dptxport_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 335static int 336dptxport_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 344static 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 415static 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 428static int 429dptxport_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 442static int 443dptxport_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 460static int 461dptxport_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 477static 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 534static 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 565static const struct apple_epic_service_ops dptxep_ops[] = { 566 { 567 .name = "AppleDCPDPTXRemotePort", 568 .init = dptxport_init, 569 .call = dptxport_call, 570 }, 571 {} 572}; 573 574int 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