1// SPDX-License-Identifier: GPL-2.0-only OR MIT 2/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */ 3/* Based on meson driver which is 4 * Copyright (C) 2016 BayLibre, SAS 5 * Author: Neil Armstrong <narmstrong@baylibre.com> 6 * Copyright (C) 2015 Amlogic, Inc. All rights reserved. 7 * Copyright (C) 2014 Endless Mobile 8 */ 9 10#include <linux/component.h> 11#include <linux/delay.h> 12#include <linux/dma-mapping.h> 13#include <linux/jiffies.h> 14#include <linux/module.h> 15#include <linux/of_address.h> 16#include <linux/of_device.h> 17 18#include <drm/drm_aperture.h> 19#include <drm/drm_atomic.h> 20#include <drm/drm_atomic_helper.h> 21#include <drm/drm_crtc.h> 22#include <drm/drm_drv.h> 23#include <drm/drm_fb_helper.h> 24#include <drm/drm_fbdev_dma.h> 25#include <drm/drm_fourcc.h> 26#include <drm/drm_fb_dma_helper.h> 27#include <drm/drm_gem_dma_helper.h> 28#include <drm/drm_gem_framebuffer_helper.h> 29#include <drm/drm_simple_kms_helper.h> 30#include <drm/drm_mode.h> 31#include <drm/drm_modeset_helper.h> 32#include <drm/drm_module.h> 33#include <drm/drm_of.h> 34#include <drm/drm_probe_helper.h> 35#include <drm/drm_vblank.h> 36#include <drm/drm_fixed.h> 37 38#include "dcp.h" 39 40#define DRIVER_NAME "apple" 41#define DRIVER_DESC "Apple display controller DRM driver" 42 43#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) 44 45#define MAX_COPROCESSORS 2 46 47struct apple_drm_private { 48 struct drm_device drm; 49}; 50 51DEFINE_DRM_GEM_DMA_FOPS(apple_fops); 52 53#define DART_PAGE_SIZE 16384 54 55static int apple_drm_gem_dumb_create(struct drm_file *file_priv, 56 struct drm_device *drm, 57 struct drm_mode_create_dumb *args) 58{ 59 args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 64); 60 args->size = round_up(args->pitch * args->height, DART_PAGE_SIZE); 61 62 return drm_gem_dma_dumb_create_internal(file_priv, drm, args); 63} 64 65static const struct drm_driver apple_drm_driver = { 66 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(apple_drm_gem_dumb_create), 67 .name = DRIVER_NAME, 68 .desc = DRIVER_DESC, 69 .date = "20221106", 70 .major = 1, 71 .minor = 0, 72 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 73 .fops = &apple_fops, 74}; 75 76static int apple_plane_atomic_check(struct drm_plane *plane, 77 struct drm_atomic_state *state) 78{ 79 struct drm_plane_state *new_plane_state; 80 struct drm_crtc_state *crtc_state; 81 82 new_plane_state = drm_atomic_get_new_plane_state(state, plane); 83 84 if (!new_plane_state->crtc) 85 return 0; 86 87 crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); 88 if (IS_ERR(crtc_state)) 89 return PTR_ERR(crtc_state); 90 91 /* 92 * DCP limits downscaling to 2x and upscaling to 4x. Attempting to 93 * scale outside these bounds errors out when swapping. 94 * 95 * This function also takes care of clipping the src/dest rectangles, 96 * which is required for correct operation. Partially off-screen 97 * surfaces may appear corrupted. 98 * 99 * DCP does not distinguish plane types in the hardware, so we set 100 * can_position. If the primary plane does not fill the screen, the 101 * hardware will fill in zeroes (black). 102 */ 103 return drm_atomic_helper_check_plane_state(new_plane_state, 104 crtc_state, 105 FRAC_16_16(1, 4), 106 FRAC_16_16(2, 1), 107 true, true); 108} 109 110static void apple_plane_atomic_update(struct drm_plane *plane, 111 struct drm_atomic_state *state) 112{ 113 /* Handled in atomic_flush */ 114} 115 116static const struct drm_plane_helper_funcs apple_plane_helper_funcs = { 117 .atomic_check = apple_plane_atomic_check, 118 .atomic_update = apple_plane_atomic_update, 119}; 120 121static void apple_plane_cleanup(struct drm_plane *plane) 122{ 123 drm_plane_cleanup(plane); 124 kfree(plane); 125} 126 127static const struct drm_plane_funcs apple_plane_funcs = { 128 .update_plane = drm_atomic_helper_update_plane, 129 .disable_plane = drm_atomic_helper_disable_plane, 130 .destroy = apple_plane_cleanup, 131 .reset = drm_atomic_helper_plane_reset, 132 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 133 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 134}; 135 136/* 137 * Table of supported formats, mapping from DRM fourccs to DCP fourccs. 138 * 139 * For future work, DCP supports more formats not listed, including YUV 140 * formats, an extra RGBA format, and a biplanar RGB10_A8 format (fourcc b3a8) 141 * used for HDR. 142 * 143 * Note: we don't have non-alpha formats but userspace breaks without XRGB. It 144 * doesn't matter for the primary plane, but cursors/overlays must not 145 * advertise formats without alpha. 146 */ 147static const u32 dcp_formats[] = { 148 DRM_FORMAT_XRGB2101010, 149 DRM_FORMAT_XRGB8888, 150 DRM_FORMAT_ARGB8888, 151 DRM_FORMAT_XBGR8888, 152 DRM_FORMAT_ABGR8888, 153}; 154 155u64 apple_format_modifiers[] = { 156 DRM_FORMAT_MOD_LINEAR, 157 DRM_FORMAT_MOD_INVALID 158}; 159 160static struct drm_plane *apple_plane_init(struct drm_device *dev, 161 unsigned long possible_crtcs, 162 enum drm_plane_type type) 163{ 164 int ret; 165 struct drm_plane *plane; 166 167 plane = kzalloc(sizeof(*plane), GFP_KERNEL); 168 169 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 170 &apple_plane_funcs, 171 dcp_formats, ARRAY_SIZE(dcp_formats), 172 apple_format_modifiers, type, NULL); 173 if (ret) 174 return ERR_PTR(ret); 175 176 drm_plane_helper_add(plane, &apple_plane_helper_funcs); 177 178 return plane; 179} 180 181static enum drm_connector_status 182apple_connector_detect(struct drm_connector *connector, bool force) 183{ 184 struct apple_connector *apple_connector = to_apple_connector(connector); 185 186 return apple_connector->connected ? connector_status_connected : 187 connector_status_disconnected; 188} 189 190static void apple_crtc_atomic_enable(struct drm_crtc *crtc, 191 struct drm_atomic_state *state) 192{ 193 struct drm_crtc_state *crtc_state; 194 crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 195 196 if (crtc_state->active_changed && crtc_state->active) { 197 struct apple_crtc *apple_crtc = to_apple_crtc(crtc); 198 dcp_poweron(apple_crtc->dcp); 199 } 200 201 if (crtc_state->active) 202 dcp_crtc_atomic_modeset(crtc, state); 203} 204 205static void apple_crtc_atomic_disable(struct drm_crtc *crtc, 206 struct drm_atomic_state *state) 207{ 208 struct drm_crtc_state *crtc_state; 209 crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 210 211 if (crtc_state->active_changed && !crtc_state->active) { 212 struct apple_crtc *apple_crtc = to_apple_crtc(crtc); 213 dcp_poweroff(apple_crtc->dcp); 214 } 215 216 if (crtc->state->event && !crtc->state->active) { 217 spin_lock_irq(&crtc->dev->event_lock); 218 drm_crtc_send_vblank_event(crtc, crtc->state->event); 219 spin_unlock_irq(&crtc->dev->event_lock); 220 221 crtc->state->event = NULL; 222 } 223} 224 225static void apple_crtc_atomic_begin(struct drm_crtc *crtc, 226 struct drm_atomic_state *state) 227{ 228 struct apple_crtc *apple_crtc = to_apple_crtc(crtc); 229 unsigned long flags; 230 231 if (crtc->state->event) { 232 spin_lock_irqsave(&crtc->dev->event_lock, flags); 233 apple_crtc->event = crtc->state->event; 234 spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 235 crtc->state->event = NULL; 236 } 237} 238 239static void dcp_atomic_commit_tail(struct drm_atomic_state *old_state) 240{ 241 struct drm_device *dev = old_state->dev; 242 243 drm_atomic_helper_commit_modeset_disables(dev, old_state); 244 245 drm_atomic_helper_commit_modeset_enables(dev, old_state); 246 247 drm_atomic_helper_commit_planes(dev, old_state, 248 DRM_PLANE_COMMIT_ACTIVE_ONLY); 249 250 drm_atomic_helper_fake_vblank(old_state); 251 252 drm_atomic_helper_commit_hw_done(old_state); 253 254 drm_atomic_helper_wait_for_flip_done(dev, old_state); 255 256 drm_atomic_helper_cleanup_planes(dev, old_state); 257} 258 259static void apple_crtc_cleanup(struct drm_crtc *crtc) 260{ 261 drm_crtc_cleanup(crtc); 262 kfree(to_apple_crtc(crtc)); 263} 264 265static const struct drm_crtc_funcs apple_crtc_funcs = { 266 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 267 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 268 .destroy = apple_crtc_cleanup, 269 .page_flip = drm_atomic_helper_page_flip, 270 .reset = drm_atomic_helper_crtc_reset, 271 .set_config = drm_atomic_helper_set_config, 272}; 273 274static const struct drm_mode_config_funcs apple_mode_config_funcs = { 275 .atomic_check = drm_atomic_helper_check, 276 .atomic_commit = drm_atomic_helper_commit, 277 .fb_create = drm_gem_fb_create, 278}; 279 280static const struct drm_mode_config_helper_funcs apple_mode_config_helpers = { 281 .atomic_commit_tail = dcp_atomic_commit_tail, 282}; 283 284static void appledrm_connector_cleanup(struct drm_connector *connector) 285{ 286 drm_connector_cleanup(connector); 287 kfree(to_apple_connector(connector)); 288} 289 290static const struct drm_connector_funcs apple_connector_funcs = { 291 .fill_modes = drm_helper_probe_single_connector_modes, 292 .destroy = appledrm_connector_cleanup, 293 .reset = drm_atomic_helper_connector_reset, 294 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 295 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 296 .detect = apple_connector_detect, 297}; 298 299static const struct drm_connector_helper_funcs apple_connector_helper_funcs = { 300 .get_modes = dcp_get_modes, 301 .mode_valid = dcp_mode_valid, 302}; 303 304static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = { 305 .atomic_begin = apple_crtc_atomic_begin, 306 .atomic_check = dcp_crtc_atomic_check, 307 .atomic_flush = dcp_flush, 308 .atomic_enable = apple_crtc_atomic_enable, 309 .atomic_disable = apple_crtc_atomic_disable, 310 .mode_fixup = dcp_crtc_mode_fixup, 311}; 312 313static int apple_probe_per_dcp(struct device *dev, 314 struct drm_device *drm, 315 struct platform_device *dcp, 316 int num, bool dcp_ext) 317{ 318 struct apple_crtc *crtc; 319 struct apple_connector *connector; 320 struct apple_encoder *enc; 321 struct drm_plane *primary; 322 int ret; 323 324 primary = apple_plane_init(drm, 1U << num, DRM_PLANE_TYPE_PRIMARY); 325 326 if (IS_ERR(primary)) 327 return PTR_ERR(primary); 328 329 crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 330 ret = drm_crtc_init_with_planes(drm, &crtc->base, primary, NULL, 331 &apple_crtc_funcs, NULL); 332 if (ret) 333 return ret; 334 335 drm_crtc_helper_add(&crtc->base, &apple_crtc_helper_funcs); 336 drm_crtc_enable_color_mgmt(&crtc->base, 0, true, 0); 337 338 enc = drmm_simple_encoder_alloc(drm, struct apple_encoder, base, 339 DRM_MODE_ENCODER_TMDS); 340 if (IS_ERR(enc)) 341 return PTR_ERR(enc); 342 enc->base.possible_crtcs = drm_crtc_mask(&crtc->base); 343 344 connector = kzalloc(sizeof(*connector), GFP_KERNEL); 345 drm_connector_helper_add(&connector->base, 346 &apple_connector_helper_funcs); 347 348#ifdef __linux__ 349 // HACK: 350 if (dcp_ext) 351 connector->base.fwnode = fwnode_handle_get(dev->fwnode); 352#endif 353 354 ret = drm_connector_init(drm, &connector->base, &apple_connector_funcs, 355 dcp_get_connector_type(dcp)); 356 if (ret) 357 return ret; 358 359 connector->base.polled = DRM_CONNECTOR_POLL_HPD; 360 connector->connected = false; 361 connector->dcp = dcp; 362 363 INIT_WORK(&connector->hotplug_wq, dcp_hotplug); 364 365 crtc->dcp = dcp; 366 dcp_link(dcp, crtc, connector); 367 368 return drm_connector_attach_encoder(&connector->base, &enc->base); 369} 370 371static int apple_get_fb_resource(struct device *dev, const char *name, 372 struct resource *fb_r) 373{ 374 int idx, ret = -ENODEV; 375 struct device_node *node; 376 377 idx = of_property_match_string(dev->of_node, "memory-region-names", name); 378 379 node = of_parse_phandle(dev->of_node, "memory-region", idx); 380 if (!node) { 381 dev_err(dev, "reserved-memory node '%s' not found\n", name); 382 return -ENODEV; 383 } 384 385 if (!of_device_is_available(node)) { 386 dev_err(dev, "reserved-memory node '%s' is unavailable\n", name); 387 goto err; 388 } 389 390 if (!of_device_is_compatible(node, "framebuffer")) { 391 dev_err(dev, "reserved-memory node '%s' is incompatible\n", 392 node->full_name); 393 goto err; 394 } 395 396 ret = of_address_to_resource(node, 0, fb_r); 397 398err: 399 of_node_put(node); 400 return ret; 401} 402 403static const struct of_device_id apple_dcp_id_tbl[] = { 404 { .compatible = "apple,dcp" }, 405 { .compatible = "apple,dcpext" }, 406 {}, 407}; 408 409static int apple_drm_init_dcp(struct device *dev) 410{ 411 struct apple_drm_private *apple = dev_get_drvdata(dev); 412 struct platform_device *dcp[MAX_COPROCESSORS]; 413 struct device_node *np; 414 u64 timeout; 415 int i, ret, num_dcp = 0; 416 417 for_each_matching_node(np, apple_dcp_id_tbl) { 418 bool dcp_ext; 419 if (!of_device_is_available(np)) { 420 of_node_put(np); 421 continue; 422 } 423 dcp_ext = of_device_is_compatible(np, "apple,dcpext"); 424 425 dcp[num_dcp] = of_find_device_by_node(np); 426 of_node_put(np); 427 if (!dcp[num_dcp]) 428 continue; 429 430 ret = apple_probe_per_dcp(dev, &apple->drm, dcp[num_dcp], 431 num_dcp, dcp_ext); 432 if (ret) 433 continue; 434 435 ret = dcp_start(dcp[num_dcp]); 436 if (ret) 437 continue; 438 439 num_dcp++; 440 } 441 442 if (num_dcp < 1) 443 return -ENODEV; 444 445 /* 446 * Starting DPTX might take some time. 447 */ 448 timeout = get_jiffies_64() + msecs_to_jiffies(3000); 449 450 for (i = 0; i < num_dcp; ++i) { 451 u64 jiffies = get_jiffies_64(); 452 u64 wait = time_after_eq64(jiffies, timeout) ? 453 0 : 454 timeout - jiffies; 455 ret = dcp_wait_ready(dcp[i], wait); 456 /* There is nothing we can do if a dcp/dcpext does not boot 457 * (successfully). Ignoring it should not do any harm now. 458 * Needs to reevaluated when adding dcpext support. 459 */ 460 if (ret) 461 dev_warn(dev, "DCP[%d] not ready: %d\n", i, ret); 462 } 463 /* HACK: Wait for dcp* to settle before a modeset */ 464 drm_msleep(100); 465 466 return 0; 467} 468 469static int apple_drm_init(struct device *dev) 470{ 471 struct apple_drm_private *apple; 472 struct resource fb_r; 473 resource_size_t fb_size; 474 int ret; 475 476 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42)); 477 if (ret) 478 return ret; 479 480 ret = apple_get_fb_resource(dev, "framebuffer", &fb_r); 481 if (ret) 482 return ret; 483 484 fb_size = fb_r.end - fb_r.start + 1; 485 ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size, 486 &apple_drm_driver); 487 if (ret) { 488 dev_err(dev, "Failed remove fb: %d\n", ret); 489 goto err_unbind; 490 } 491 492#ifdef __linux__ 493 apple = devm_drm_dev_alloc(dev, &apple_drm_driver, 494 struct apple_drm_private, drm); 495 if (IS_ERR(apple)) 496 return PTR_ERR(apple); 497#else 498 struct apldrm_softc *sc = (struct apldrm_softc *)dev; 499 apple = (struct apple_drm_private *)&sc->sc_ddev; 500#endif 501 502 dev_set_drvdata(dev, apple); 503 504 ret = component_bind_all(dev, apple); 505 if (ret) 506 return ret; 507 508 ret = drmm_mode_config_init(&apple->drm); 509 if (ret) 510 goto err_unbind; 511 512 /* 513 * IOMFB::UPPipeDCP_H13P::verify_surfaces produces the error "plane 514 * requires a minimum of 32x32 for the source buffer" if smaller 515 */ 516 apple->drm.mode_config.min_width = 32; 517 apple->drm.mode_config.min_height = 32; 518 519 /* 520 * TODO: this is the max framebuffer size not the maximal supported 521 * output resolution. DCP reports the maximal framebuffer size take it 522 * from there. 523 * Hardcode it for now to the M1 Max DCP reported 'MaxSrcBufferWidth' 524 * and 'MaxSrcBufferHeight' of 16384. 525 */ 526 apple->drm.mode_config.max_width = 16384; 527 apple->drm.mode_config.max_height = 16384; 528 529 apple->drm.mode_config.funcs = &apple_mode_config_funcs; 530 apple->drm.mode_config.helper_private = &apple_mode_config_helpers; 531 532 ret = apple_drm_init_dcp(dev); 533 if (ret) 534 goto err_unbind; 535 536 drm_mode_config_reset(&apple->drm); 537 538 ret = drm_dev_register(&apple->drm, 0); 539 if (ret) 540 goto err_unbind; 541 542 drm_fbdev_dma_setup(&apple->drm, 32); 543 544 return 0; 545 546err_unbind: 547 component_unbind_all(dev, NULL); 548 return ret; 549} 550 551static void apple_drm_uninit(struct device *dev) 552{ 553 struct apple_drm_private *apple = dev_get_drvdata(dev); 554 555 drm_dev_unregister(&apple->drm); 556 drm_atomic_helper_shutdown(&apple->drm); 557 558 component_unbind_all(dev, NULL); 559 560 dev_set_drvdata(dev, NULL); 561} 562 563static int apple_drm_bind(struct device *dev) 564{ 565 return apple_drm_init(dev); 566} 567 568static void apple_drm_unbind(struct device *dev) 569{ 570 apple_drm_uninit(dev); 571} 572 573const struct component_master_ops apple_drm_ops = { 574 .bind = apple_drm_bind, 575 .unbind = apple_drm_unbind, 576}; 577 578static int add_dcp_components(struct device *dev, 579 struct component_match **matchptr) 580{ 581 struct device_node *np; 582 int num = 0; 583 584 for_each_matching_node(np, apple_dcp_id_tbl) { 585 if (of_device_is_available(np)) { 586 drm_of_component_match_add(dev, matchptr, 587 component_compare_of, np); 588 num++; 589 } 590 of_node_put(np); 591 } 592 593 return num; 594} 595 596static int apple_platform_probe(struct platform_device *pdev) 597{ 598 struct device *mdev = &pdev->dev; 599 struct component_match *match = NULL; 600 int num_dcp; 601 602 /* add DCP components, handle less than 1 as probe error */ 603 num_dcp = add_dcp_components(mdev, &match); 604 if (num_dcp < 1) 605 return -ENODEV; 606 607 return component_master_add_with_match(mdev, &apple_drm_ops, match); 608} 609 610#ifdef __linux__ 611 612static int apple_platform_remove(struct platform_device *pdev) 613{ 614 component_master_del(&pdev->dev, &apple_drm_ops); 615 616 return 0; 617} 618 619static const struct of_device_id of_match[] = { 620 { .compatible = "apple,display-subsystem" }, 621 {} 622}; 623MODULE_DEVICE_TABLE(of, of_match); 624 625#endif 626 627#ifdef CONFIG_PM_SLEEP 628static int apple_platform_suspend(struct device *dev) 629{ 630 struct apple_drm_private *apple = dev_get_drvdata(dev); 631 632 if (apple) 633 return drm_mode_config_helper_suspend(&apple->drm); 634 635 return 0; 636} 637 638static int apple_platform_resume(struct device *dev) 639{ 640 struct apple_drm_private *apple = dev_get_drvdata(dev); 641 642 if (apple) 643 drm_mode_config_helper_resume(&apple->drm); 644 645 return 0; 646} 647 648static const struct dev_pm_ops apple_platform_pm_ops = { 649 .suspend = apple_platform_suspend, 650 .resume = apple_platform_resume, 651}; 652#endif 653 654#ifdef __linux__ 655 656static struct platform_driver apple_platform_driver = { 657 .driver = { 658 .name = "apple-drm", 659 .of_match_table = of_match, 660#ifdef CONFIG_PM_SLEEP 661 .pm = &apple_platform_pm_ops, 662#endif 663 }, 664 .probe = apple_platform_probe, 665 .remove = apple_platform_remove, 666}; 667 668drm_module_platform_driver(apple_platform_driver); 669 670MODULE_AUTHOR("Alyssa Rosenzweig <alyssa@rosenzweig.io>"); 671MODULE_DESCRIPTION(DRIVER_DESC); 672MODULE_LICENSE("Dual MIT/GPL"); 673 674#endif 675