xref: /openbsd/sys/dev/pci/drm/apple/dcp.c (revision 44a5d259)
1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
3 
4 #include <linux/align.h>
5 #include <linux/apple-mailbox.h>
6 #include <linux/bitmap.h>
7 #include <linux/clk.h>
8 #include <linux/completion.h>
9 #include <linux/component.h>
10 #include <linux/delay.h>
11 #include <linux/dma-mapping.h>
12 #include <linux/gpio/consumer.h>
13 #include <linux/iommu.h>
14 #include <linux/jiffies.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/moduleparam.h>
18 #include <linux/of_address.h>
19 #include <linux/of_device.h>
20 #include <linux/of_platform.h>
21 #include <linux/slab.h>
22 #include <linux/soc/apple/rtkit.h>
23 #include <linux/string.h>
24 #include <linux/workqueue.h>
25 
26 #include <drm/drm_fb_dma_helper.h>
27 #include <drm/drm_fourcc.h>
28 #include <drm/drm_framebuffer.h>
29 #include <drm/drm_module.h>
30 #include <drm/drm_probe_helper.h>
31 #include <drm/drm_vblank.h>
32 
33 #include "afk.h"
34 #include "dcp.h"
35 #include "dcp-internal.h"
36 #include "iomfb.h"
37 #include "parser.h"
38 #include "trace.h"
39 
40 #define APPLE_DCP_COPROC_CPU_CONTROL	 0x44
41 #define APPLE_DCP_COPROC_CPU_CONTROL_RUN BIT(4)
42 
43 #define DCP_BOOT_TIMEOUT msecs_to_jiffies(1000)
44 
45 static bool show_notch;
46 module_param(show_notch, bool, 0644);
47 MODULE_PARM_DESC(show_notch, "Use the full display height and shows the notch");
48 
49 /* HACK: moved here to avoid circular dependency between apple_drv and dcp */
dcp_drm_crtc_vblank(struct apple_crtc * crtc)50 void dcp_drm_crtc_vblank(struct apple_crtc *crtc)
51 {
52 	unsigned long flags;
53 
54 	spin_lock_irqsave(&crtc->base.dev->event_lock, flags);
55 	if (crtc->event) {
56 		drm_crtc_send_vblank_event(&crtc->base, crtc->event);
57 		crtc->event = NULL;
58 	}
59 	spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags);
60 }
61 
dcp_set_dimensions(struct apple_dcp * dcp)62 void dcp_set_dimensions(struct apple_dcp *dcp)
63 {
64 	int i;
65 	int width_mm = dcp->width_mm;
66 	int height_mm = dcp->height_mm;
67 
68 	if (width_mm == 0 || height_mm == 0) {
69 		width_mm = dcp->panel.width_mm;
70 		height_mm = dcp->panel.height_mm;
71 	}
72 
73 	/* Set the connector info */
74 	if (dcp->connector) {
75 		struct drm_connector *connector = &dcp->connector->base;
76 
77 		mutex_lock(&connector->dev->mode_config.mutex);
78 		connector->display_info.width_mm = width_mm;
79 		connector->display_info.height_mm = height_mm;
80 		mutex_unlock(&connector->dev->mode_config.mutex);
81 	}
82 
83 	/*
84 	 * Fix up any probed modes. Modes are created when parsing
85 	 * TimingElements, dimensions are calculated when parsing
86 	 * DisplayAttributes, and TimingElements may be sent first
87 	 */
88 	for (i = 0; i < dcp->nr_modes; ++i) {
89 		dcp->modes[i].mode.width_mm = width_mm;
90 		dcp->modes[i].mode.height_mm = height_mm;
91 	}
92 }
93 
dcp_has_panel(struct apple_dcp * dcp)94 bool dcp_has_panel(struct apple_dcp *dcp)
95 {
96 	return dcp->panel.width_mm > 0;
97 }
98 
99 /*
100  * Helper to send a DRM vblank event. We do not know how call swap_submit_dcp
101  * without surfaces. To avoid timeouts in drm_atomic_helper_wait_for_vblanks
102  * send a vblank event via a workqueue.
103  */
dcp_delayed_vblank(struct work_struct * work)104 static void dcp_delayed_vblank(struct work_struct *work)
105 {
106 	struct apple_dcp *dcp;
107 
108 	dcp = container_of(work, struct apple_dcp, vblank_wq);
109 	mdelay(5);
110 	dcp_drm_crtc_vblank(dcp->crtc);
111 }
112 
dcp_recv_msg(void * cookie,u8 endpoint,u64 message)113 static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message)
114 {
115 	struct apple_dcp *dcp = cookie;
116 
117 	trace_dcp_recv_msg(dcp, endpoint, message);
118 
119 	switch (endpoint) {
120 	case IOMFB_ENDPOINT:
121 		return iomfb_recv_msg(dcp, message);
122 	case SYSTEM_ENDPOINT:
123 		afk_receive_message(dcp->systemep, message);
124 		return;
125 	case DISP0_ENDPOINT:
126 		afk_receive_message(dcp->ibootep, message);
127 		return;
128 	case DPTX_ENDPOINT:
129 		afk_receive_message(dcp->dptxep, message);
130 		return;
131 	default:
132 		WARN(endpoint, "unknown DCP endpoint %hhu\n", endpoint);
133 	}
134 }
135 
dcp_rtk_crashed(void * cookie)136 static void dcp_rtk_crashed(void *cookie)
137 {
138 	struct apple_dcp *dcp = cookie;
139 
140 	dcp->crashed = true;
141 	dev_err(dcp->dev, "DCP has crashed\n");
142 	if (dcp->connector) {
143 		dcp->connector->connected = 0;
144 		schedule_work(&dcp->connector->hotplug_wq);
145 	}
146 	complete(&dcp->start_done);
147 }
148 
dcp_rtk_shmem_setup(void * cookie,struct apple_rtkit_shmem * bfr)149 static int dcp_rtk_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr)
150 {
151 	struct apple_dcp *dcp = cookie;
152 
153 	if (bfr->iova) {
154 		struct iommu_domain *domain =
155 			iommu_get_domain_for_dev(dcp->dev);
156 		phys_addr_t phy_addr;
157 
158 		if (!domain)
159 			return -ENOMEM;
160 
161 		// TODO: get map from device-tree
162 		phy_addr = iommu_iova_to_phys(domain, bfr->iova);
163 		if (!phy_addr)
164 			return -ENOMEM;
165 
166 		// TODO: verify phy_addr, cache attribute
167 		bfr->buffer = memremap(phy_addr, bfr->size, MEMREMAP_WB);
168 		if (!bfr->buffer)
169 			return -ENOMEM;
170 
171 		bfr->is_mapped = true;
172 		dev_info(dcp->dev,
173 			 "shmem_setup: iova: %lx -> pa: %lx -> iomem: %lx\n",
174 			 (uintptr_t)bfr->iova, (uintptr_t)phy_addr,
175 			 (uintptr_t)bfr->buffer);
176 	} else {
177 		bfr->buffer = dma_alloc_coherent(dcp->dev, bfr->size,
178 						 &bfr->iova, GFP_KERNEL);
179 		if (!bfr->buffer)
180 			return -ENOMEM;
181 
182 		dev_info(dcp->dev, "shmem_setup: iova: %lx, buffer: %lx\n",
183 			 (uintptr_t)bfr->iova, (uintptr_t)bfr->buffer);
184 	}
185 
186 	return 0;
187 }
188 
dcp_rtk_shmem_destroy(void * cookie,struct apple_rtkit_shmem * bfr)189 static void dcp_rtk_shmem_destroy(void *cookie, struct apple_rtkit_shmem *bfr)
190 {
191 	struct apple_dcp *dcp = cookie;
192 
193 	if (bfr->is_mapped)
194 		memunmap(bfr->buffer);
195 	else
196 		dma_free_coherent(dcp->dev, bfr->size, bfr->buffer, bfr->iova);
197 }
198 
199 static struct apple_rtkit_ops rtkit_ops = {
200 	.crashed = dcp_rtk_crashed,
201 	.recv_message = dcp_recv_msg,
202 	.shmem_setup = dcp_rtk_shmem_setup,
203 	.shmem_destroy = dcp_rtk_shmem_destroy,
204 };
205 
dcp_send_message(struct apple_dcp * dcp,u8 endpoint,u64 message)206 void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message)
207 {
208 	trace_dcp_send_msg(dcp, endpoint, message);
209 	apple_rtkit_send_message(dcp->rtk, endpoint, message, NULL,
210 				 true);
211 }
212 
dcp_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)213 int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
214 {
215 	struct platform_device *pdev = to_apple_crtc(crtc)->dcp;
216 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
217 	struct drm_plane_state *new_state;
218 	struct drm_plane *plane;
219 	struct drm_crtc_state *crtc_state;
220 	int plane_idx, plane_count = 0;
221 	bool needs_modeset;
222 
223 	if (dcp->crashed)
224 		return -EINVAL;
225 
226 	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
227 
228 	needs_modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;
229 	if (!needs_modeset && !dcp->connector->connected) {
230 		dev_err(dcp->dev, "crtc_atomic_check: disconnected but no modeset\n");
231 		return -EINVAL;
232 	}
233 
234 	for_each_new_plane_in_state(state, plane, new_state, plane_idx) {
235 		/* skip planes not for this crtc */
236 		if (new_state->crtc != crtc)
237 			continue;
238 
239 		plane_count += 1;
240 	}
241 
242 	if (plane_count > DCP_MAX_PLANES) {
243 		dev_err(dcp->dev, "crtc_atomic_check: Blend supports only 2 layers!\n");
244 		return -EINVAL;
245 	}
246 
247 	return 0;
248 }
249 EXPORT_SYMBOL_GPL(dcp_crtc_atomic_check);
250 
dcp_get_connector_type(struct platform_device * pdev)251 int dcp_get_connector_type(struct platform_device *pdev)
252 {
253 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
254 
255 	return (dcp->connector_type);
256 }
257 EXPORT_SYMBOL_GPL(dcp_get_connector_type);
258 
259 #define DPTX_CONNECT_TIMEOUT msecs_to_jiffies(1000)
260 
dcp_dptx_connect(struct apple_dcp * dcp,u32 port)261 static int dcp_dptx_connect(struct apple_dcp *dcp, u32 port)
262 {
263 	int ret = 0;
264 
265 	if (!dcp->phy) {
266 		dev_warn(dcp->dev, "dcp_dptx_connect: missing phy\n");
267 		return -ENODEV;
268 	}
269 	dev_info(dcp->dev, "%s(port=%d)\n", __func__, port);
270 
271 	mutex_lock(&dcp->hpd_mutex);
272 	if (!dcp->dptxport[port].enabled) {
273 		dev_warn(dcp->dev, "dcp_dptx_connect: dptx service for port %d not enabled\n", port);
274 		ret = -ENODEV;
275 		goto out_unlock;
276 	}
277 
278 	if (dcp->dptxport[port].connected)
279 		goto out_unlock;
280 
281 	reinit_completion(&dcp->dptxport[port].linkcfg_completion);
282 	dcp->dptxport[port].atcphy = dcp->phy;
283 	dptxport_connect(dcp->dptxport[port].service, 0, dcp->dptx_phy, dcp->dptx_die);
284 	dptxport_request_display(dcp->dptxport[port].service);
285 	dcp->dptxport[port].connected = true;
286 
287 	mutex_unlock(&dcp->hpd_mutex);
288 	ret = wait_for_completion_timeout(&dcp->dptxport[port].linkcfg_completion,
289 				    DPTX_CONNECT_TIMEOUT);
290 	if (ret < 0)
291 		dev_warn(dcp->dev, "dcp_dptx_connect: port %d link complete failed:%d\n",
292 			 port, ret);
293 	else
294 		dev_dbg(dcp->dev, "dcp_dptx_connect: waited %d ms for link\n",
295 			jiffies_to_msecs(DPTX_CONNECT_TIMEOUT - ret));
296 
297 	usleep_range(5, 10);
298 
299 	return 0;
300 
301 out_unlock:
302 	mutex_unlock(&dcp->hpd_mutex);
303 	return ret;
304 }
305 
dcp_dptx_disconnect(struct apple_dcp * dcp,u32 port)306 static int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port)
307 {
308 	dev_info(dcp->dev, "%s(port=%d)\n", __func__, port);
309 
310 	mutex_lock(&dcp->hpd_mutex);
311 	if (dcp->dptxport[port].enabled && dcp->dptxport[port].connected) {
312 		dptxport_release_display(dcp->dptxport[port].service);
313 		dcp->dptxport[port].connected = false;
314 	}
315 	mutex_unlock(&dcp->hpd_mutex);
316 
317 	return 0;
318 }
319 
dcp_dp2hdmi_hpd(int irq,void * data)320 static irqreturn_t dcp_dp2hdmi_hpd(int irq, void *data)
321 {
322 	struct apple_dcp *dcp = data;
323 	bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
324 
325 	/* do nothing on disconnect and trust that dcp detects it itself.
326 	 * Parallel disconnect HPDs result drm disabling the CRTC even when it
327 	 * should not.
328 	 * The interrupt should be changed to rising but for now the disconnect
329 	 * IRQs might be helpful for debugging.
330 	 */
331 	dev_info(dcp->dev, "DP2HDMI HPD irq, connected:%d\n", connected);
332 
333 	if (connected)
334 		dcp_dptx_connect(dcp, 0);
335 
336 	return IRQ_HANDLED;
337 }
338 
dcp_link(struct platform_device * pdev,struct apple_crtc * crtc,struct apple_connector * connector)339 void dcp_link(struct platform_device *pdev, struct apple_crtc *crtc,
340 	      struct apple_connector *connector)
341 {
342 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
343 
344 	dcp->crtc = crtc;
345 	dcp->connector = connector;
346 }
347 EXPORT_SYMBOL_GPL(dcp_link);
348 
dcp_start(struct platform_device * pdev)349 int dcp_start(struct platform_device *pdev)
350 {
351 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
352 	int ret;
353 
354 	init_completion(&dcp->start_done);
355 
356 	/* start RTKit endpoints */
357 	ret = systemep_init(dcp);
358 	if (ret)
359 		dev_warn(dcp->dev, "Failed to start system endpoint: %d\n", ret);
360 
361 	if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) {
362 		ret = ibootep_init(dcp);
363 		if (ret)
364 			dev_warn(dcp->dev, "Failed to start IBOOT endpoint: %d\n",
365 				 ret);
366 
367 		ret = dptxep_init(dcp);
368 		if (ret)
369 			dev_warn(dcp->dev, "Failed to start DPTX endpoint: %d\n",
370 				 ret);
371 		else if (dcp->dptxport[0].enabled) {
372 			bool connected;
373 			/* force disconnect on start - necessary if the display
374 			 * is already up from m1n1
375 			 */
376 			dptxport_set_hpd(dcp->dptxport[0].service, false);
377 			dptxport_release_display(dcp->dptxport[0].service);
378 			usleep_range(10 * USEC_PER_MSEC, 25 * USEC_PER_MSEC);
379 
380 			connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
381 			dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected);
382 
383 			// necessary on j473/j474 but not on j314c
384 			if (connected)
385 				dcp_dptx_connect(dcp, 0);
386 		}
387 	} else if (dcp->phy)
388 		dev_warn(dcp->dev, "OS firmware incompatible with dptxport EP\n");
389 
390 	ret = iomfb_start_rtkit(dcp);
391 	if (ret)
392 		dev_err(dcp->dev, "Failed to start IOMFB endpoint: %d\n", ret);
393 
394 	return ret;
395 }
396 EXPORT_SYMBOL(dcp_start);
397 
dcp_enable_dp2hdmi_hpd(struct apple_dcp * dcp)398 static int dcp_enable_dp2hdmi_hpd(struct apple_dcp *dcp)
399 {
400 	if (dcp->hdmi_hpd_irq)
401 		enable_irq(dcp->hdmi_hpd_irq);
402 
403 	return 0;
404 }
405 
dcp_wait_ready(struct platform_device * pdev,u64 timeout)406 int dcp_wait_ready(struct platform_device *pdev, u64 timeout)
407 {
408 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
409 	int ret;
410 
411 	if (dcp->crashed)
412 		return -ENODEV;
413 	if (dcp->active)
414 		return dcp_enable_dp2hdmi_hpd(dcp);;
415 	if (timeout <= 0)
416 		return -ETIMEDOUT;
417 
418 	ret = wait_for_completion_timeout(&dcp->start_done, timeout);
419 	if (ret < 0)
420 		return ret;
421 
422 	if (dcp->crashed)
423 		return -ENODEV;
424 
425 	if (dcp->active)
426 		dcp_enable_dp2hdmi_hpd(dcp);
427 
428 	return dcp->active ? 0 : -ETIMEDOUT;
429 }
430 EXPORT_SYMBOL(dcp_wait_ready);
431 
dcp_sleep(struct apple_dcp * dcp)432 static void __maybe_unused dcp_sleep(struct apple_dcp *dcp)
433 {
434 	switch (dcp->fw_compat) {
435 	case DCP_FIRMWARE_V_12_3:
436 		iomfb_sleep_v12_3(dcp);
437 		break;
438 	case DCP_FIRMWARE_V_13_5:
439 		iomfb_sleep_v13_3(dcp);
440 		break;
441 	default:
442 		WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
443 		break;
444 	}
445 }
446 
dcp_poweron(struct platform_device * pdev)447 void dcp_poweron(struct platform_device *pdev)
448 {
449 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
450 
451 	if (dcp->hdmi_hpd) {
452 		bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
453 		dev_info(dcp->dev, "%s: DP2HDMI HPD connected:%d\n", __func__, connected);
454 
455 		if (connected)
456 			dcp_dptx_connect(dcp, 0);
457 	}
458 
459 	switch (dcp->fw_compat) {
460 	case DCP_FIRMWARE_V_12_3:
461 		iomfb_poweron_v12_3(dcp);
462 		break;
463 	case DCP_FIRMWARE_V_13_5:
464 		iomfb_poweron_v13_3(dcp);
465 		break;
466 	default:
467 		WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
468 		break;
469 	}
470 }
471 EXPORT_SYMBOL(dcp_poweron);
472 
dcp_poweroff(struct platform_device * pdev)473 void dcp_poweroff(struct platform_device *pdev)
474 {
475 	struct apple_dcp *dcp = platform_get_drvdata(pdev);
476 
477 	switch (dcp->fw_compat) {
478 	case DCP_FIRMWARE_V_12_3:
479 		iomfb_poweroff_v12_3(dcp);
480 		break;
481 	case DCP_FIRMWARE_V_13_5:
482 		iomfb_poweroff_v13_3(dcp);
483 		break;
484 	default:
485 		WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
486 		break;
487 	}
488 
489 	if (dcp->hdmi_hpd) {
490 		bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
491 		if (!connected)
492 			dcp_dptx_disconnect(dcp, 0);
493 	}
494 }
495 EXPORT_SYMBOL(dcp_poweroff);
496 
dcp_work_register_backlight(struct work_struct * work)497 static void dcp_work_register_backlight(struct work_struct *work)
498 {
499 	int ret;
500 	struct apple_dcp *dcp;
501 
502 	dcp = container_of(work, struct apple_dcp, bl_register_wq);
503 
504 	mutex_lock(&dcp->bl_register_mutex);
505 	if (dcp->brightness.bl_dev)
506 		goto out_unlock;
507 
508 	/* try to register backlight device, */
509 	ret = dcp_backlight_register(dcp);
510 	if (ret) {
511 		dev_err(dcp->dev, "Unable to register backlight device\n");
512 		dcp->brightness.maximum = 0;
513 	}
514 
515 out_unlock:
516 	mutex_unlock(&dcp->bl_register_mutex);
517 }
518 
dcp_work_update_backlight(struct work_struct * work)519 static void dcp_work_update_backlight(struct work_struct *work)
520 {
521 	struct apple_dcp *dcp;
522 
523 	dcp = container_of(work, struct apple_dcp, bl_update_wq);
524 
525 	dcp_backlight_update(dcp);
526 }
527 
dcp_create_piodma_iommu_dev(struct apple_dcp * dcp)528 static int dcp_create_piodma_iommu_dev(struct apple_dcp *dcp)
529 {
530 	int ret;
531 	struct device_node *node = of_get_child_by_name(dcp->dev->of_node, "piodma");
532 
533 	if (!node)
534 		return dev_err_probe(dcp->dev, -ENODEV,
535 				     "Failed to get piodma child DT node\n");
536 
537 	dcp->piodma = of_platform_device_create(node, NULL, dcp->dev);
538 	if (!dcp->piodma) {
539 		of_node_put(node);
540 		return dev_err_probe(dcp->dev, -ENODEV, "Failed to gcreate piodma pdev for %pOF\n", node);
541 	}
542 
543 	ret = dma_set_mask_and_coherent(&dcp->piodma->dev, DMA_BIT_MASK(42));
544 	if (ret)
545 		goto err_destroy_pdev;
546 
547 	ret = of_dma_configure(&dcp->piodma->dev, node, true);
548 	if (ret) {
549 		ret = dev_err_probe(dcp->dev, ret,
550 			"Failed to configure IOMMU child DMA\n");
551 		goto err_destroy_pdev;
552 	}
553 	of_node_put(node);
554 
555 	dcp->iommu_dom = iommu_domain_alloc(&platform_bus_type);
556 	if (!dcp->iommu_dom) {
557 		ret = -ENOMEM;
558 		goto err_destroy_pdev;
559 	}
560 
561 	ret = iommu_attach_device(dcp->iommu_dom, &dcp->piodma->dev);
562 	if (ret) {
563 		ret = dev_err_probe(dcp->dev, ret,
564 					"Failed to attach IOMMU child domain\n");
565 		goto err_free_domain;
566 	}
567 
568 	return 0;
569 err_free_domain:
570 	iommu_domain_free(dcp->iommu_dom);
571 err_destroy_pdev:
572 	of_node_put(node);
573 	of_platform_device_destroy(&dcp->piodma->dev, NULL);
574 	return ret;
575 }
576 
dcp_get_bw_scratch_reg(struct apple_dcp * dcp,u32 expected)577 static int dcp_get_bw_scratch_reg(struct apple_dcp *dcp, u32 expected)
578 {
579 	struct of_phandle_args ph_args;
580 	u32 addr_idx, disp_idx, offset;
581 	int ret;
582 
583 	ret = of_parse_phandle_with_args(dcp->dev->of_node, "apple,bw-scratch",
584 				   "#apple,bw-scratch-cells", 0, &ph_args);
585 	if (ret < 0) {
586 		dev_err(dcp->dev, "Failed to read 'apple,bw-scratch': %d\n", ret);
587 		return ret;
588 	}
589 
590 	if (ph_args.args_count != 3) {
591 		dev_err(dcp->dev, "Unexpected 'apple,bw-scratch' arg count %d\n",
592 			ph_args.args_count);
593 		ret = -EINVAL;
594 		goto err_of_node_put;
595 	}
596 
597 	addr_idx = ph_args.args[0];
598 	disp_idx = ph_args.args[1];
599 	offset = ph_args.args[2];
600 
601 	if (disp_idx != expected || disp_idx >= MAX_DISP_REGISTERS) {
602 		dev_err(dcp->dev, "Unexpected disp_reg value in 'apple,bw-scratch': %d\n",
603 			disp_idx);
604 		ret = -EINVAL;
605 		goto err_of_node_put;
606 	}
607 
608 	ret = of_address_to_resource(ph_args.np, addr_idx, &dcp->disp_bw_scratch_res);
609 	if (ret < 0) {
610 		dev_err(dcp->dev, "Failed to get 'apple,bw-scratch' resource %d from %pOF\n",
611 			addr_idx, ph_args.np);
612 		goto err_of_node_put;
613 	}
614 	if (offset > resource_size(&dcp->disp_bw_scratch_res) - 4) {
615 		ret = -EINVAL;
616 		goto err_of_node_put;
617 	}
618 
619 	dcp->disp_registers[disp_idx] = &dcp->disp_bw_scratch_res;
620 	dcp->disp_bw_scratch_index = disp_idx;
621 	dcp->disp_bw_scratch_offset = offset;
622 	ret = 0;
623 
624 err_of_node_put:
625 	of_node_put(ph_args.np);
626 	return ret;
627 }
628 
dcp_get_bw_doorbell_reg(struct apple_dcp * dcp,u32 expected)629 static int dcp_get_bw_doorbell_reg(struct apple_dcp *dcp, u32 expected)
630 {
631 	struct of_phandle_args ph_args;
632 	u32 addr_idx, disp_idx;
633 	int ret;
634 
635 	ret = of_parse_phandle_with_args(dcp->dev->of_node, "apple,bw-doorbell",
636 				   "#apple,bw-doorbell-cells", 0, &ph_args);
637 	if (ret < 0) {
638 		dev_err(dcp->dev, "Failed to read 'apple,bw-doorbell': %d\n", ret);
639 		return ret;
640 	}
641 
642 	if (ph_args.args_count != 2) {
643 		dev_err(dcp->dev, "Unexpected 'apple,bw-doorbell' arg count %d\n",
644 			ph_args.args_count);
645 		ret = -EINVAL;
646 		goto err_of_node_put;
647 	}
648 
649 	addr_idx = ph_args.args[0];
650 	disp_idx = ph_args.args[1];
651 
652 	if (disp_idx != expected || disp_idx >= MAX_DISP_REGISTERS) {
653 		dev_err(dcp->dev, "Unexpected disp_reg value in 'apple,bw-doorbell': %d\n",
654 			disp_idx);
655 		ret = -EINVAL;
656 		goto err_of_node_put;
657 	}
658 
659 	ret = of_address_to_resource(ph_args.np, addr_idx, &dcp->disp_bw_doorbell_res);
660 	if (ret < 0) {
661 		dev_err(dcp->dev, "Failed to get 'apple,bw-doorbell' resource %d from %pOF\n",
662 			addr_idx, ph_args.np);
663 		goto err_of_node_put;
664 	}
665 	dcp->disp_bw_doorbell_index = disp_idx;
666 	dcp->disp_registers[disp_idx] = &dcp->disp_bw_doorbell_res;
667 	ret = 0;
668 
669 err_of_node_put:
670 	of_node_put(ph_args.np);
671 	return ret;
672 }
673 
dcp_get_disp_regs(struct apple_dcp * dcp)674 static int dcp_get_disp_regs(struct apple_dcp *dcp)
675 {
676 	struct platform_device *pdev = to_platform_device(dcp->dev);
677 	int count = pdev->num_resources - 1;
678 	int i, ret;
679 
680 	if (count <= 0 || count > MAX_DISP_REGISTERS)
681 		return -EINVAL;
682 
683 	for (i = 0; i < count; ++i) {
684 		dcp->disp_registers[i] =
685 			platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
686 	}
687 
688 	/* load pmgr bandwidth scratch resource and offset */
689 	ret = dcp_get_bw_scratch_reg(dcp, count);
690 	if (ret < 0)
691 		return ret;
692 	count += 1;
693 
694 	/* load pmgr bandwidth doorbell resource if present (only on t8103) */
695 	if (of_property_present(dcp->dev->of_node, "apple,bw-doorbell")) {
696 		ret = dcp_get_bw_doorbell_reg(dcp, count);
697 		if (ret < 0)
698 			return ret;
699 		count += 1;
700 	}
701 
702 	dcp->nr_disp_registers = count;
703 	return 0;
704 }
705 
706 #define DCP_FW_VERSION_MIN_LEN	3
707 #define DCP_FW_VERSION_MAX_LEN	5
708 #define DCP_FW_VERSION_STR_LEN	(DCP_FW_VERSION_MAX_LEN * 4)
709 
dcp_read_fw_version(struct device * dev,const char * name,char * version_str)710 static int dcp_read_fw_version(struct device *dev, const char *name,
711 			       char *version_str)
712 {
713 	u32 ver[DCP_FW_VERSION_MAX_LEN];
714 	int len_str;
715 	int len;
716 
717 	len = of_property_read_variable_u32_array(dev->of_node, name, ver,
718 						  DCP_FW_VERSION_MIN_LEN,
719 						  DCP_FW_VERSION_MAX_LEN);
720 
721 	switch (len) {
722 	case 3:
723 		len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN,
724 				    "%d.%d.%d", ver[0], ver[1], ver[2]);
725 		break;
726 	case 4:
727 		len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN,
728 				    "%d.%d.%d.%d", ver[0], ver[1], ver[2],
729 				    ver[3]);
730 		break;
731 	case 5:
732 		len_str = scnprintf(version_str, DCP_FW_VERSION_STR_LEN,
733 				    "%d.%d.%d.%d.%d", ver[0], ver[1], ver[2],
734 				    ver[3], ver[4]);
735 		break;
736 	default:
737 		len_str = strscpy(version_str, "UNKNOWN",
738 				  DCP_FW_VERSION_STR_LEN);
739 		if (len >= 0)
740 			len = -EOVERFLOW;
741 		break;
742 	}
743 
744 	if (len_str >= DCP_FW_VERSION_STR_LEN)
745 		dev_warn(dev, "'%s' truncated: '%s'\n", name, version_str);
746 
747 	return len;
748 }
749 
dcp_check_firmware_version(struct device * dev)750 static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev)
751 {
752 	char compat_str[DCP_FW_VERSION_STR_LEN];
753 	char fw_str[DCP_FW_VERSION_STR_LEN];
754 	int ret;
755 
756 	/* firmware version is just informative */
757 	dcp_read_fw_version(dev, "apple,firmware-version", fw_str);
758 
759 	ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str);
760 	if (ret < 0) {
761 		dev_err(dev, "Could not read 'apple,firmware-compat': %d\n", ret);
762 		return DCP_FIRMWARE_UNKNOWN;
763 	}
764 
765 	if (strncmp(compat_str, "12.3.0", sizeof(compat_str)) == 0)
766 		return DCP_FIRMWARE_V_12_3;
767 	/*
768 	 * m1n1 reports firmware version 13.5 as compatible with 13.3. This is
769 	 * only true for the iomfb endpoint. The interface for the dptx-port
770 	 * endpoint changed between 13.3 and 13.5. The driver will only support
771 	 * firmware 13.5. Check the actual firmware version for compat version
772 	 * 13.3 until m1n1 reports 13.5 as "firmware-compat".
773 	 */
774 	else if ((strncmp(compat_str, "13.3.0", sizeof(compat_str)) == 0) &&
775 		 (strncmp(fw_str, "13.5.0", sizeof(compat_str)) == 0))
776 		return DCP_FIRMWARE_V_13_5;
777 	else if (strncmp(compat_str, "13.5.0", sizeof(compat_str)) == 0)
778 		return DCP_FIRMWARE_V_13_5;
779 
780 	dev_err(dev, "DCP firmware-compat %s (FW: %s) is not supported\n",
781 		compat_str, fw_str);
782 
783 	return DCP_FIRMWARE_UNKNOWN;
784 }
785 
dcp_comp_bind(struct device * dev,struct device * main,void * data)786 static int dcp_comp_bind(struct device *dev, struct device *main, void *data)
787 {
788 	struct device_node *panel_np;
789 	struct apple_dcp *dcp = dev_get_drvdata(dev);
790 	u32 cpu_ctrl;
791 	int ret;
792 
793 	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42));
794 	if (ret)
795 		return ret;
796 
797 	dcp->coproc_reg = devm_platform_ioremap_resource_byname(to_platform_device(dev), "coproc");
798 	if (IS_ERR(dcp->coproc_reg))
799 		return PTR_ERR(dcp->coproc_reg);
800 
801 	of_property_read_u32(dev->of_node, "apple,dcp-index",
802 					   &dcp->index);
803 	of_property_read_u32(dev->of_node, "apple,dptx-phy",
804 					   &dcp->dptx_phy);
805 	of_property_read_u32(dev->of_node, "apple,dptx-die",
806 					   &dcp->dptx_die);
807 	if (dcp->index || dcp->dptx_phy || dcp->dptx_die)
808 		dev_info(dev, "DCP index:%u dptx target phy: %u dptx die: %u\n",
809 			 dcp->index, dcp->dptx_phy, dcp->dptx_die);
810 	rw_init(&dcp->hpd_mutex, "aplhpd");
811 
812 	if (!show_notch)
813 		ret = of_property_read_u32(dev->of_node, "apple,notch-height",
814 					   &dcp->notch_height);
815 
816 	if (dcp->notch_height > MAX_NOTCH_HEIGHT)
817 		dcp->notch_height = MAX_NOTCH_HEIGHT;
818 	if (dcp->notch_height > 0)
819 		dev_info(dev, "Detected display with notch of %u pixel\n", dcp->notch_height);
820 
821 	/* initialize brightness scale to a sensible default to avoid divide by 0*/
822 	dcp->brightness.scale = 65536;
823 	panel_np = of_get_compatible_child(dev->of_node, "apple,panel-mini-led");
824 	if (panel_np)
825 		dcp->panel.has_mini_led = true;
826 	else
827 		panel_np = of_get_compatible_child(dev->of_node, "apple,panel");
828 
829 	if (panel_np) {
830 		const char height_prop[2][16] = { "adj-height-mm", "height-mm" };
831 
832 		if (of_device_is_available(panel_np)) {
833 			ret = of_property_read_u32(panel_np, "apple,max-brightness",
834 						   &dcp->brightness.maximum);
835 			if (ret)
836 				dev_err(dev, "Missing property 'apple,max-brightness'\n");
837 		}
838 
839 		of_property_read_u32(panel_np, "width-mm", &dcp->panel.width_mm);
840 		/* use adjusted height as long as the notch is hidden */
841 		of_property_read_u32(panel_np, height_prop[!dcp->notch_height],
842 				     &dcp->panel.height_mm);
843 
844 		of_node_put(panel_np);
845 		dcp->connector_type = DRM_MODE_CONNECTOR_eDP;
846 		INIT_WORK(&dcp->bl_register_wq, dcp_work_register_backlight);
847 		rw_init(&dcp->bl_register_mutex, "dcpbl");
848 		INIT_WORK(&dcp->bl_update_wq, dcp_work_update_backlight);
849 	} else if (of_property_match_string(dev->of_node, "apple,connector-type", "HDMI-A") >= 0)
850 		dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA;
851 	else if (of_property_match_string(dev->of_node, "apple,connector-type", "DP") >= 0)
852 		dcp->connector_type = DRM_MODE_CONNECTOR_DisplayPort;
853 	else if (of_property_match_string(dev->of_node, "apple,connector-type", "USB-C") >= 0)
854 		dcp->connector_type = DRM_MODE_CONNECTOR_USB;
855 	else
856 		dcp->connector_type = DRM_MODE_CONNECTOR_Unknown;
857 
858 	ret = dcp_create_piodma_iommu_dev(dcp);
859 	if (ret)
860 		return dev_err_probe(dev, ret,
861 				"Failed to created PIODMA iommu child device");
862 
863 	ret = dcp_get_disp_regs(dcp);
864 	if (ret) {
865 		dev_err(dev, "failed to find display registers\n");
866 		return ret;
867 	}
868 
869 	dcp->clk = devm_clk_get(dev, NULL);
870 	if (IS_ERR(dcp->clk))
871 		return dev_err_probe(dev, PTR_ERR(dcp->clk),
872 				     "Unable to find clock\n");
873 
874 	bitmap_zero(dcp->memdesc_map, DCP_MAX_MAPPINGS);
875 	// TDOD: mem_desc IDs start at 1, for simplicity just skip '0' entry
876 	set_bit(0, dcp->memdesc_map);
877 
878 	INIT_WORK(&dcp->vblank_wq, dcp_delayed_vblank);
879 
880 	dcp->swapped_out_fbs =
881 		(struct list_head)LIST_HEAD_INIT(dcp->swapped_out_fbs);
882 
883 	cpu_ctrl =
884 		readl_relaxed(dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL);
885 	writel_relaxed(cpu_ctrl | APPLE_DCP_COPROC_CPU_CONTROL_RUN,
886 		       dcp->coproc_reg + APPLE_DCP_COPROC_CPU_CONTROL);
887 
888 	dcp->rtk = devm_apple_rtkit_init(dev, dcp, "mbox", 0, &rtkit_ops);
889 	if (IS_ERR(dcp->rtk))
890 		return dev_err_probe(dev, PTR_ERR(dcp->rtk),
891 				     "Failed to initialize RTKit\n");
892 
893 	ret = apple_rtkit_wake(dcp->rtk);
894 	if (ret)
895 		return dev_err_probe(dev, ret,
896 				     "Failed to boot RTKit: %d\n", ret);
897 	return ret;
898 }
899 
900 /*
901  * We need to shutdown DCP before tearing down the display subsystem. Otherwise
902  * the DCP will crash and briefly flash a green screen of death.
903  */
dcp_comp_unbind(struct device * dev,struct device * main,void * data)904 static void dcp_comp_unbind(struct device *dev, struct device *main, void *data)
905 {
906 	struct apple_dcp *dcp = dev_get_drvdata(dev);
907 
908 	if (dcp->hdmi_hpd_irq)
909 		disable_irq(dcp->hdmi_hpd_irq);
910 
911 	if (dcp && dcp->shmem)
912 		iomfb_shutdown(dcp);
913 
914 	if (dcp->piodma) {
915 		iommu_detach_device(dcp->iommu_dom, &dcp->piodma->dev);
916 		iommu_domain_free(dcp->iommu_dom);
917 		/* TODO: the piodma platform device has to be destroyed but
918 		 *       doing so leads to all kind of breakage.
919 		 */
920 		// of_platform_device_destroy(&dcp->piodma->dev, NULL);
921 		dcp->piodma = NULL;
922 	}
923 
924 	devm_clk_put(dev, dcp->clk);
925 	dcp->clk = NULL;
926 }
927 
928 static const struct component_ops dcp_comp_ops = {
929 	.bind	= dcp_comp_bind,
930 	.unbind	= dcp_comp_unbind,
931 };
932 
dcp_platform_probe(struct platform_device * pdev)933 static int dcp_platform_probe(struct platform_device *pdev)
934 {
935 	enum dcp_firmware_version fw_compat;
936 	struct device *dev = &pdev->dev;
937 	struct apple_dcp *dcp;
938 	u32 mux_index;
939 
940 	fw_compat = dcp_check_firmware_version(dev);
941 	if (fw_compat == DCP_FIRMWARE_UNKNOWN)
942 		return -ENODEV;
943 
944 	/* Check for "apple,bw-scratch" to avoid probing appledrm with outdated
945 	 * device trees. This prevents replacing simpledrm and ending up without
946 	 * display.
947 	 */
948 	if (!of_property_present(dev->of_node, "apple,bw-scratch"))
949 		return dev_err_probe(dev, -ENODEV, "Incompatible devicetree! "
950 			"Use devicetree matching this kernel.\n");
951 
952 	dcp = devm_kzalloc(dev, sizeof(*dcp), GFP_KERNEL);
953 	if (!dcp)
954 		return -ENOMEM;
955 
956 	dcp->fw_compat = fw_compat;
957 	dcp->dev = dev;
958 	dcp->hw = *(struct apple_dcp_hw_data *)of_device_get_match_data(dev);
959 
960 	platform_set_drvdata(pdev, dcp);
961 
962 	dcp->phy = devm_phy_optional_get(dev, "dp-phy");
963 	if (IS_ERR(dcp->phy)) {
964 		dev_err(dev, "Failed to get dp-phy: %ld\n", PTR_ERR(dcp->phy));
965 		return PTR_ERR(dcp->phy);
966 	}
967 	if (dcp->phy) {
968 		int ret;
969 		/*
970 		 * Request DP2HDMI related GPIOs as optional for DP-altmode
971 		 * compatibility. J180D misses a dp2hdmi-pwren GPIO in the
972 		 * template ADT. TODO: check device ADT
973 		 */
974 		dcp->hdmi_hpd = devm_gpiod_get_optional(dev, "hdmi-hpd", GPIOD_IN);
975 		if (IS_ERR(dcp->hdmi_hpd))
976 			return PTR_ERR(dcp->hdmi_hpd);
977 		if (dcp->hdmi_hpd) {
978 			int irq = gpiod_to_irq(dcp->hdmi_hpd);
979 			if (irq < 0) {
980 				dev_err(dev, "failed to translate HDMI hpd GPIO to IRQ\n");
981 				return irq;
982 			}
983 			dcp->hdmi_hpd_irq = irq;
984 
985 			ret = devm_request_threaded_irq(dev, dcp->hdmi_hpd_irq,
986 						NULL, dcp_dp2hdmi_hpd,
987 						IRQF_ONESHOT | IRQF_NO_AUTOEN |
988 						IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
989 						"dp2hdmi-hpd-irq", dcp);
990 			if (ret < 0) {
991 				dev_err(dev, "failed to request HDMI hpd irq %d: %d\n",
992 					irq, ret);
993 				return ret;
994 			}
995 		}
996 
997 		/*
998 		 * Power DP2HDMI on as it is required for the HPD irq.
999 		 * TODO: check if one is sufficient for the hpd to save power
1000 		 *       on battery powered Macbooks.
1001 		 */
1002 		dcp->hdmi_pwren = devm_gpiod_get_optional(dev, "hdmi-pwren", GPIOD_OUT_HIGH);
1003 		if (IS_ERR(dcp->hdmi_pwren))
1004 			return PTR_ERR(dcp->hdmi_pwren);
1005 
1006 		dcp->dp2hdmi_pwren = devm_gpiod_get_optional(dev, "dp2hdmi-pwren", GPIOD_OUT_HIGH);
1007 		if (IS_ERR(dcp->dp2hdmi_pwren))
1008 			return PTR_ERR(dcp->dp2hdmi_pwren);
1009 
1010 		ret = of_property_read_u32(dev->of_node, "mux-index", &mux_index);
1011 		if (!ret) {
1012 			dcp->xbar = devm_mux_control_get(dev, "dp-xbar");
1013 			if (IS_ERR(dcp->xbar)) {
1014 				dev_err(dev, "Failed to get dp-xbar: %ld\n", PTR_ERR(dcp->xbar));
1015 				return PTR_ERR(dcp->xbar);
1016 			}
1017 			ret = mux_control_select(dcp->xbar, mux_index);
1018 			if (ret)
1019 				dev_warn(dev, "mux_control_select failed: %d\n", ret);
1020 		}
1021 	}
1022 
1023 	return component_add(&pdev->dev, &dcp_comp_ops);
1024 }
1025 
1026 #ifdef __linux__
1027 
dcp_platform_remove(struct platform_device * pdev)1028 static int dcp_platform_remove(struct platform_device *pdev)
1029 {
1030 	component_del(&pdev->dev, &dcp_comp_ops);
1031 
1032 	return 0;
1033 }
1034 
dcp_platform_shutdown(struct platform_device * pdev)1035 static void dcp_platform_shutdown(struct platform_device *pdev)
1036 {
1037 	component_del(&pdev->dev, &dcp_comp_ops);
1038 }
1039 
1040 #endif
1041 
dcp_platform_suspend(struct device * dev)1042 static int dcp_platform_suspend(struct device *dev)
1043 {
1044 	struct apple_dcp *dcp = dev_get_drvdata(dev);
1045 
1046 	if (dcp->hdmi_hpd_irq) {
1047 		disable_irq(dcp->hdmi_hpd_irq);
1048 		dcp_dptx_disconnect(dcp, 0);
1049 	}
1050 	/*
1051 	 * Set the device as a wakeup device, which forces its power
1052 	 * domains to stay on. We need this as we do not support full
1053 	 * shutdown properly yet.
1054 	 */
1055 	device_set_wakeup_path(dev);
1056 
1057 	return 0;
1058 }
1059 
dcp_platform_resume(struct device * dev)1060 static int dcp_platform_resume(struct device *dev)
1061 {
1062 	struct apple_dcp *dcp = dev_get_drvdata(dev);
1063 
1064 	if (dcp->hdmi_hpd_irq)
1065 		enable_irq(dcp->hdmi_hpd_irq);
1066 
1067 	if (dcp->hdmi_hpd) {
1068 		bool connected = gpiod_get_value_cansleep(dcp->hdmi_hpd);
1069 		dev_info(dcp->dev, "resume: HPD connected:%d\n", connected);
1070 		if (connected)
1071 			dcp_dptx_connect(dcp, 0);
1072 	}
1073 
1074 	return 0;
1075 }
1076 
1077 static DEFINE_SIMPLE_DEV_PM_OPS(dcp_platform_pm_ops,
1078 				dcp_platform_suspend, dcp_platform_resume);
1079 
1080 
1081 static const struct apple_dcp_hw_data apple_dcp_hw_t6020 = {
1082 	.num_dptx_ports = 1,
1083 };
1084 
1085 static const struct apple_dcp_hw_data apple_dcp_hw_t8112 = {
1086 	.num_dptx_ports = 2,
1087 };
1088 
1089 static const struct apple_dcp_hw_data apple_dcp_hw_dcp = {
1090 	.num_dptx_ports = 0,
1091 };
1092 
1093 static const struct apple_dcp_hw_data apple_dcp_hw_dcpext = {
1094 	.num_dptx_ports = 2,
1095 };
1096 
1097 static const struct of_device_id of_match[] = {
1098 	{ .compatible = "apple,t6020-dcp", .data = &apple_dcp_hw_t6020,  },
1099 	{ .compatible = "apple,t8112-dcp", .data = &apple_dcp_hw_t8112,  },
1100 	{ .compatible = "apple,dcp",       .data = &apple_dcp_hw_dcp,    },
1101 	{ .compatible = "apple,dcpext",    .data = &apple_dcp_hw_dcpext, },
1102 	{}
1103 };
1104 MODULE_DEVICE_TABLE(of, of_match);
1105 
1106 #ifdef __linux__
1107 
1108 static struct platform_driver apple_platform_driver = {
1109 	.probe		= dcp_platform_probe,
1110 	.remove		= dcp_platform_remove,
1111 	.shutdown	= dcp_platform_shutdown,
1112 	.driver	= {
1113 		.name = "apple-dcp",
1114 		.of_match_table	= of_match,
1115 		.pm = pm_sleep_ptr(&dcp_platform_pm_ops),
1116 	},
1117 };
1118 
1119 drm_module_platform_driver(apple_platform_driver);
1120 
1121 MODULE_AUTHOR("Alyssa Rosenzweig <alyssa@rosenzweig.io>");
1122 MODULE_DESCRIPTION("Apple Display Controller DRM driver");
1123 MODULE_LICENSE("Dual MIT/GPL");
1124 
1125 #endif
1126