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