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