From 949a211aa6fb1796ae7ba9273a7b5921ad59e7b5 Mon Sep 17 00:00:00 2001 From: Sean Dolbec Date: Sat, 7 Nov 2026 20:07:43 -0500 Subject: [PATCH 1/4] iommu: apple-dart: adopt T8132 main external display handoff A T8132 main DCP wired to fixed HDMI uses six firmware segments and shares the retained SID 0/SID 16 frontend contract with the external display path. Qualify that layout from the available main DCP and its connector role. Validate its physical framebuffer separately from inherited mappings and preserve both frontend streams without a board-name gate. Assisted-by: LLM checkpatch Signed-off-by: Sean Dolbec --- drivers/iommu/apple-dart-dcp-format.h | 17 ++++++++++++- drivers/iommu/apple-dart-dcp-shared.h | 12 ++++++--- drivers/iommu/apple-dart-dcp.h | 26 ++++++++++++++++--- drivers/iommu/apple-dart.c | 36 ++++++++++++++++++++++++--- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/apple-dart-dcp-format.h b/drivers/iommu/apple-dart-dcp-format.h index 52b853b59d973..a2344756b2111 100644 --- a/drivers/iommu/apple-dart-dcp-format.h +++ b/drivers/iommu/apple-dart-dcp-format.h @@ -54,6 +54,8 @@ struct dcp_adt_instance { static const u32 dcp_adt_kinds_internal[] = {1, 0, 10, 2, 2, 2, 2}; static const u32 dcp_adt_kinds_external[] = {1, 0, 10, 2}; +/* T8132 main external DCP (iop-dcp-nub lists six segments). */ +static const u32 dcp_adt_kinds_main_external[] = {1, 0, 10, 2, 2, 2}; static const struct dcp_adt_instance dcp_adt_internal = { .dart = "dart-dcp", @@ -65,6 +67,17 @@ static const struct dcp_adt_instance dcp_adt_internal = { .num_segments = ARRAY_SIZE(dcp_adt_kinds_internal), }; +/* Main external DCP: internal node names and six firmware segments. */ +static const struct dcp_adt_instance dcp_adt_main_external = { + .dart = "dart-dcp", + .dcp = "dcp", + .nub = "iop-dcp-nub", + .disp = "dart-disp0", + .segment_names = "__TEXT;__DATA;__OS_LOG;unknown;unknown;unknown", + .kinds = dcp_adt_kinds_main_external, + .num_segments = ARRAY_SIZE(dcp_adt_kinds_main_external), +}; + /* The external DCPs run the same firmware with a single extra segment. */ static const struct dcp_adt_instance dcp_adt_external[] = { { @@ -383,10 +396,12 @@ static inline int dcp_adt_parse_instance(const void *data, size_t size, /* Parse metadata only: live MMIO, RAM exclusion and page tables remain unproven. */ static inline int dcp_adt_parse_frontend(const void *data, size_t size, + const struct dcp_adt_instance *inst, struct dcp_adt_tables *firmware, struct dcp_adt_frontend *frontend) { - int ret = dcp_adt_parse_common(data, size, &dcp_adt_internal, firmware, frontend); + int ret = dcp_adt_parse_common(data, size, inst ? inst : &dcp_adt_internal, + firmware, frontend); if (ret) return ret; diff --git a/drivers/iommu/apple-dart-dcp-shared.h b/drivers/iommu/apple-dart-dcp-shared.h index 2396ac5e1d33c..94b01309ab162 100644 --- a/drivers/iommu/apple-dart-dcp-shared.h +++ b/drivers/iommu/apple-dart-dcp-shared.h @@ -280,7 +280,8 @@ static int __apple_dart_dcp_dispext_map(struct device *piodma, unsigned int sid, if (ret) return ret; pio = to_dart_domain(iommu_get_domain_for_dev(piodma))->dcp; - if (pio->sid != 16 || !pio->dart->dispext) + if (pio->sid != 16 || + (!pio->dart->dispext && !pio->dart->t8132_main_external)) return -ENODEV; dcp = sid ? pio : pio->dart->frontend[0]; if (!dcp) @@ -369,7 +370,8 @@ int apple_dart_dcp_dispext_stream_ok(struct device *piodma, unsigned int sid) if (!domain || domain->owner != &apple_dart_iommu_ops || (sid != 0 && sid != 16)) return -ENODEV; pio = to_dart_domain(domain)->dcp; - if (!pio || pio->sid != 16 || !pio->dart->dispext) + if (!pio || pio->sid != 16 || + (!pio->dart->dispext && !pio->dart->t8132_main_external)) return -ENODEV; dcp = sid ? pio : pio->dart->frontend[0]; if (!dcp) @@ -403,7 +405,8 @@ static void *__apple_dart_dcp_dispext_scanout_alloc(struct device *piodma, size_ if (ret) return ERR_PTR(ret); pio = to_dart_domain(iommu_get_domain_for_dev(piodma))->dcp; - if (pio->sid != 16 || !pio->dart->dispext) + if (pio->sid != 16 || + (!pio->dart->dispext && !pio->dart->t8132_main_external)) return ERR_PTR(-ENODEV); dcp = pio->dart->frontend[0]; if (!dcp) @@ -515,7 +518,8 @@ static struct apple_dart_dcp *apple_dart_dcp_dispext_pio(struct device *piodma) if (!domain || domain->owner != &apple_dart_iommu_ops) return NULL; pio = to_dart_domain(domain)->dcp; - if (!pio || pio->sid != 16 || !pio->dart->dispext) + if (!pio || pio->sid != 16 || + (!pio->dart->dispext && !pio->dart->t8132_main_external)) return NULL; return pio; } diff --git a/drivers/iommu/apple-dart-dcp.h b/drivers/iommu/apple-dart-dcp.h index 49ed48c2f3de6..2da29fd1ad65c 100644 --- a/drivers/iommu/apple-dart-dcp.h +++ b/drivers/iommu/apple-dart-dcp.h @@ -297,7 +297,8 @@ static int apple_dart_dcp_read_adt(struct apple_dart *dart, struct dcp_adt_table ret = dcp_adt_parse_common(data, resource_size(&res), dart->dcp_instance, tables, frontend); else - ret = frontend ? dcp_adt_parse_frontend(data, resource_size(&res), tables, + ret = frontend ? dcp_adt_parse_frontend(data, resource_size(&res), + dart->dcp_instance, tables, frontend) : dcp_adt_parse_instance(data, resource_size(&res), dart->dcp_instance, tables); @@ -734,7 +735,9 @@ static int apple_dart_frontend_validate(struct apple_dart_dcp *dcp, { struct device_node *alias; struct of_phandle_args spec; - u32 wanted = dcp->sid == 0 ? BIT(4) | BIT(6) : BIT(5); + bool main_external = dcp->dart->dcp_instance == &dcp_adt_main_external; + u32 wanted = dcp->sid == 0 ? + (main_external ? BIT(4) : BIT(4) | BIT(6)) : BIT(5); u32 seen = 0; unsigned long flags; int nr, ret; @@ -768,7 +771,9 @@ static int apple_dart_frontend_validate(struct apple_dart_dcp *dcp, alias = of_find_node_by_path(dcp->sid == 0 ? "disp0" : "disp0_piodma"); ret = alias && consumer == alias && of_device_is_available(alias) && - of_property_read_bool(alias, "apple,j713-inherited-mappings") ? 0 : -ENODEV; + (main_external || + of_property_read_bool(alias, "apple,j713-inherited-mappings")) ? + 0 : -ENODEV; of_node_put(alias); if (ret) return ret; @@ -793,6 +798,9 @@ static int apple_dart_frontend_validate(struct apple_dart_dcp *dcp, struct resource res; const __be32 *maps; unsigned int matches = 0; + bool retained_framebuffer = main_external && dcp->sid == 0 && + of_property_match_string(consumer, "memory-region-names", + "framebuffer") == i; int bytes; if (!node) @@ -806,6 +814,15 @@ static int apple_dart_frontend_validate(struct apple_dart_dcp *dcp, rmem->base != res.start || rmem->size != resource_size(&res)) goto put_node; maps = of_get_property(node, "iommu-addresses", &bytes); + /* + * The retained scanout framebuffer is a physical handoff, not a + * frontend-DART segment. Keep requiring the reserved no-map region, + * but only region94 participates in SID0's inherited table contract. + */ + if (retained_framebuffer) { + ret = maps ? -EINVAL : 0; + goto put_node; + } if (!maps || bytes <= 0 || bytes % (5 * sizeof(*maps))) goto put_node; for (int j = 0; j < bytes / sizeof(*maps); j += 5) { @@ -1068,7 +1085,8 @@ static int apple_dart_dcp_validate(struct apple_dart_dcp *dcp, struct device *de } if (!of_device_is_compatible(dev->of_node, - dcp->dart->dcp_instance == &dcp_adt_internal ? + (dcp->dart->dcp_instance == &dcp_adt_internal || + dcp->dart->dcp_instance == &dcp_adt_main_external) ? "apple,t8132-dcp" : "apple,t8132-dcpext") || !apple_dart_dcp_firmware(dev->of_node) || (dcp->owner && dcp->owner != dev)) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 7cf99361bf796..40ceb6ffd0530 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -438,6 +438,7 @@ struct apple_dart { /* The display coprocessor whose firmware DART (SID23) this is. */ const struct dcp_adt_instance *dcp_instance; bool preserve_display; + bool t8132_main_external; /* A J713 external display DART, borrowed from its external DCP */ bool dispext; /* @@ -853,6 +854,21 @@ static bool apple_dart_frontend_enabled; module_param_named(j713_display, apple_dart_frontend_enabled, bool, 0600); MODULE_PARM_DESC(j713_display, "Enable explicit J713 display DART admission after arming watchdog"); +/* A fixed HDMI connector makes the T8132 main DCP an external pipeline. */ +static bool apple_dart_t8132_main_external(void) +{ + struct device_node *dcp; + const char *connector; + bool qualified; + + dcp = of_find_compatible_node(NULL, NULL, "apple,t8132-dcp"); + qualified = dcp && of_device_is_available(dcp) && + !of_property_read_string(dcp, "apple,connector-type", &connector) && + !strcmp(connector, "HDMI-A"); + of_node_put(dcp); + return qualified; +} + static int apple_dart_dcp_invalidate_tlb(struct apple_dart_stream_map *stream_map); #include "apple-dart-dcp.h" @@ -5068,6 +5084,7 @@ static int apple_dart_probe(struct platform_device *pdev) of_node_name_prefix(dev->of_node->parent, "usb4-pcie-tunnel-"); bool j713_display, j713_dcpext, j713_dispext, j713_radio; + bool t8132_main_external; /* * The M4 internal display DARTs are live: DCP firmware (SID23) @@ -5078,6 +5095,7 @@ static int apple_dart_probe(struct platform_device *pdev) j713_display = res && !tunneled && apple_t8132_firmware_path() && of_device_is_compatible(dev->of_node, "apple,t8132-dart") && (res->start == 0x30a300000ULL || res->start == 0x30a340000ULL); + t8132_main_external = j713_display && apple_dart_t8132_main_external(); /* The external DCP firmware DARTs (SID23) of dcpext0 and dcpext1. */ j713_dcpext = res && !tunneled && apple_t8132_firmware_path() && of_device_is_compatible(dev->of_node, "apple,t8132-dart") && @@ -5105,7 +5123,7 @@ static int apple_dart_probe(struct platform_device *pdev) if (j713_display && !IS_ENABLED(CONFIG_APPLE_DART_J713_DCP)) return dev_err_probe(dev, -EOPNOTSUPP, "retained J713 display requires CONFIG_APPLE_DART_J713_DCP=y\n"); - if (j713_display && + if (j713_display && !t8132_main_external && ((res->start == 0x30a300000ULL && !READ_ONCE(apple_dart_frontend_enabled)) || (res->start == 0x30a340000ULL && !READ_ONCE(apple_dart_dcp_enabled)))) { dev_info(dev, "Leaving inherited display DART untouched; explicit guarded probe required\n"); @@ -5198,6 +5216,7 @@ static int apple_dart_probe(struct platform_device *pdev) if (ret) return ret; dart->preserve_display = j713_display || j713_dcpext || j713_dispext; + dart->t8132_main_external = t8132_main_external; dart->dispext = j713_dispext; dart->power_gated = j713_dispext; if (j713_display && of_property_present(dev->of_node, "apple,clock-protection")) { @@ -5219,7 +5238,8 @@ static int apple_dart_probe(struct platform_device *pdev) else if (j713_dispext) dart->dcp_instance = &dcp_adt_external[res->start == 0x48a300000ULL]; else if (j713_display) - dart->dcp_instance = &dcp_adt_internal; + dart->dcp_instance = t8132_main_external ? + &dcp_adt_main_external : &dcp_adt_internal; if (tunneled) { pd_np = of_parse_phandle(dev->of_node, "power-domains", 0); if (pd_np) { @@ -7045,7 +7065,17 @@ int apple_dart_dcp_dispext_state(struct device_node *consumer) if (!device_is_bound(&pdev->dev) || pdev->dev.driver != &apple_dart_driver.driver) goto out; dart = platform_get_drvdata(pdev); - if (!dart || !dart->dispext || !dart->frontend[1]) + if (!dart || !dart->frontend[1]) + goto out; + if (!dart->dispext && dart->t8132_main_external) { + if (!dart->frontend[0] || !apple_dart_dcp_registers(dart->frontend[0]) || + !apple_dart_dcp_registers(dart->frontend[1]) || apple_dart_fault_pending(dart)) + ret = -EIO; + else + ret = 0; + goto out; + } + if (!dart->dispext) goto out; ret = apple_dart_dispext_verify(dart, "after power-up"); out: From 28d4f2a5b0fe194c3efaa7a4c5de343f0ec185cd Mon Sep 17 00:00:00 2001 From: Sean Dolbec Date: Sat, 7 Nov 2026 20:07:44 -0500 Subject: [PATCH 2/4] drm: apple: dcp: adopt a T8132 main external output Some T8132 desktops route the main DCP through a fixed DPTX-to-HDMI path instead of an internal panel. Use the connector-type property as that role contract, prepare the PHY and converter rails, adopt the retained firmware session, and register it through the external-output KMS path. Accept the retained physical framebuffer without manual bind controls or machine-name checks. Use a J773g M4 profile without panel timing and continue to refuse machines without a profile. J773g does not use the J713 panel admission parameters because the external path skips apple_pmp_j713_display_prepare() and has no internal panel timing or panel power admission. Keep an empty external device from replacing simpledrm, and retain an adopted session when its initial HDMI DPTX connect fails. The external KMS layer still caps the device at two outputs, so two adopted dcpext outputs leave the fixed HDMI output unregistered. Assisted-by: LLM checkpatch Signed-off-by: Sean Dolbec --- drivers/gpu/drm/apple/apple_drv.c | 71 +++++++++++++---- drivers/gpu/drm/apple/dcp-m4-profile.h | 15 ++++ drivers/gpu/drm/apple/dcp.c | 78 +++++++++++++++++-- drivers/gpu/drm/apple/dcp.h | 1 + drivers/gpu/drm/apple/iomfb_m4.c | 16 ++-- drivers/gpu/drm/apple/t8132_ext_drm.c | 29 ++++++- .../drivers/gpu/apple-dcp/lifecycle.py | 8 ++ 7 files changed, 193 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 5e25242419499..3b8a1b8da90bf 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -1345,6 +1345,8 @@ static int apple_probe_typec_ports(struct drm_device *drm, return 0; } +static bool apple_t8132_main_external_present(void); + static int apple_get_fb_resource(struct device *dev, const char *name, struct resource *fb_r) { @@ -1364,7 +1366,9 @@ static int apple_get_fb_resource(struct device *dev, const char *name, goto err; } - if (!of_device_is_compatible(node, "framebuffer")) { + if (!of_device_is_compatible(node, "framebuffer") && + !(apple_t8132_main_external_present() && + of_device_is_compatible(node, "apple,asc-mem"))) { dev_err(dev, "reserved-memory node '%s' is incompatible\n", node->full_name); goto err; @@ -1425,6 +1429,16 @@ static const struct of_device_id apple_dcp_id_tbl[] = { {}, }; +static bool apple_t8132_main_external_present(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "apple,t8132-dcp"); + bool present = dcp_t8132_main_external(np) && of_device_is_available(np); + + of_node_put(np); + return present; +} + /* * A supported firmware-described external provider participates in component * matching. A T6030 external display processor is a pipe of this DRM device @@ -1433,7 +1447,7 @@ static const struct of_device_id apple_dcp_id_tbl[] = { */ static bool apple_dcp_usable(struct device_node *np) { - return of_device_is_available(np) && + return of_device_is_available(np) && !dcp_t8132_main_external(np) && (!of_device_is_compatible(np, "apple,t6030-dcpext") || dcp_t6030_ext_native(np)) && (!of_device_is_compatible(np, "apple,t8140-dcpext") || @@ -1488,6 +1502,15 @@ static void apple_dcp_start_fn(struct work_struct *work) w->ret = dcp_start(w->dcp); } +static bool apple_drm_has_output(struct drm_device *drm) +{ + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, drm) + return true; + return false; +} + static int apple_drm_init_dcp(struct device *dev) { struct apple_drm_private *apple = dev_get_drvdata(dev); @@ -1545,8 +1568,17 @@ static int apple_drm_init_dcp(struct device *dev) num_dcp++; } - if (num_dcp < 1) - return -ENODEV; + if (num_dcp < 1) { + if (!apple_t8132_main_external_present()) + return -ENODEV; + ret = t8132_ext_drm_init(&apple->drm); + if (ret) + dev_warn(dev, "T8132 main external output was not registered: %d\n", + ret); + else if (!apple_drm_has_output(&apple->drm)) + ret = -ENODEV; + return ret; + } /* * Build the Type-C port connectors before starting any pipeline: a @@ -1682,9 +1714,11 @@ static int apple_drm_init(struct device *dev) apple->drm.mode_config.funcs = &apple_mode_config_funcs; apple->drm.mode_config.helper_private = &apple_mode_config_helpers; - ret = component_bind_all(dev, apple); - if (ret) - return ret; + if (!apple_t8132_main_external_present()) { + ret = component_bind_all(dev, apple); + if (ret) + return ret; + } ret = apple_drm_init_dcp(dev); if (ret) @@ -1802,7 +1836,8 @@ static int apple_drm_init(struct device *dev) err_unbind: t8132_ext_drm_fini(&apple->drm); apple_drm_quiesce_connectors(&apple->drm); - component_unbind_all(dev, NULL); + if (!apple_t8132_main_external_present()) + component_unbind_all(dev, NULL); dcp_unlink(&apple->drm); return ret; } @@ -1825,7 +1860,8 @@ static void apple_drm_uninit(struct device *dev) drm_dev_unplug(&apple->drm); drm_atomic_helper_shutdown(&apple->drm); - component_unbind_all(dev, NULL); + if (!apple_t8132_main_external_present()) + component_unbind_all(dev, NULL); dcp_unlink(&apple->drm); dev_set_drvdata(dev, NULL); @@ -1900,10 +1936,14 @@ static int apple_platform_probe(struct platform_device *pdev) /* add DCP components, handle less than 1 as probe error */ num_dcp = add_dcp_components(mdev, &match); - if (num_dcp < 1) - return -ENODEV; - - ret = component_master_add_with_match(mdev, &apple_drm_ops, match); + if (num_dcp < 1) { + if (!apple_t8132_main_external_present()) + return -ENODEV; + ret = apple_drm_init(mdev); + } else { + ret = component_master_add_with_match(mdev, &apple_drm_ops, + match); + } if (ret) return ret; @@ -1919,7 +1959,10 @@ static int apple_platform_probe(struct platform_device *pdev) static void apple_platform_remove(struct platform_device *pdev) { pm_vt_switch_unregister(&pdev->dev); - component_master_del(&pdev->dev, &apple_drm_ops); + if (apple_t8132_main_external_present()) + apple_drm_uninit(&pdev->dev); + else + component_master_del(&pdev->dev, &apple_drm_ops); } static const struct of_device_id of_match[] = { diff --git a/drivers/gpu/drm/apple/dcp-m4-profile.h b/drivers/gpu/drm/apple/dcp-m4-profile.h index 438f734f87d06..54952950e1941 100644 --- a/drivers/gpu/drm/apple/dcp-m4-profile.h +++ b/drivers/gpu/drm/apple/dcp-m4-profile.h @@ -58,6 +58,20 @@ static const struct dcp_m4_profile dcp_m4_profile_j713 = { .timing = dcp_m4_timing_j713, .timing_elements = 1, }; +/* The J773g uses the shared firmware ABI for an external output, not a panel. */ +static const struct dcp_m4_profile dcp_m4_profile_j773g = { + .name = "J773g", .machine = "apple,j773g", + .panel_width = 2560, .panel_height = 1664, .notch_rows = 64, + .boot_stride = 10240, + .swap_size = 0x1bd8, .swap_surface = 0x588, .swap_dva = 0xe38, + .swap_nullable = 0x1bcb, .swap_tail = 0x1bd6, .swap_nits = 0x35e, + .swap_intent = 0x17c, + .disabled_color = 0xff000000, + .done_method = 590, .done_size = 0x730, .open_method = 457, + .brightness_max = 500, .brightness_default = 120, + .width_mm = 0, .height_mm = 0, +}; + /* The J715's panel timing, as macOS reports it (TimingElements, ID 2). */ static const u32 dcp_m4_timing_j715[2][8] = { { 2880, 2960, 8, 32, 40, 7710975, 1, 0 }, @@ -106,6 +120,7 @@ static const struct dcp_m4_profile dcp_m4_profile_j604 = { static const struct dcp_m4_profile *const dcp_m4_profiles[] = { &dcp_m4_profile_j713, + &dcp_m4_profile_j773g, &dcp_m4_profile_j715, &dcp_m4_profile_j604, }; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index e9aaf1f1884fe..fbb7d78816238 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -5456,6 +5456,13 @@ static int dcp_connector_type_from_dt(struct device_node *np) return DRM_MODE_CONNECTOR_Unknown; } +/* A T8132 main DCP can serve a fixed external output instead of a panel. */ +bool dcp_t8132_main_external(struct device_node *np) +{ + return np && of_device_is_compatible(np, "apple,t8132-dcp") && + dcp_connector_type_from_dt(np) == DRM_MODE_CONNECTOR_HDMIA; +} + /* * The fixed route's crossbar selection and Type-C mux are taken at probe and * must be released with the device: a crossbar left selected blocks the next @@ -6337,6 +6344,47 @@ static int dcp_desktop_probe_routes(struct apple_dcp *dcp) return 0; } +/* + * A fixed HDMI main DCP reaches its sink through a T8122-style DPTX PHY and + * two converter power GPIOs. Require every resource and leave it powered for + * the retained firmware session. + */ +static int dcp_m4_hdmi_prepare(struct apple_dcp *dcp) +{ + struct device *dev = dcp->dev; + int ret; + + dcp->hw.num_dptx_ports = 1; + dcp->phy = devm_phy_get(dev, "dp-phy"); + if (IS_ERR(dcp->phy)) + return dev_err_probe(dev, PTR_ERR(dcp->phy), + "HDMI DP PHY unavailable\n"); + dcp->fixed_phy = dcp->phy; + ret = phy_init(dcp->phy); + if (ret) + return dev_err_probe(dev, ret, "HDMI DP PHY init failed\n"); + ret = phy_power_on(dcp->phy); + if (ret) { + phy_exit(dcp->phy); + return dev_err_probe(dev, ret, "HDMI DP PHY power failed\n"); + } + ret = devm_add_action_or_reset(dev, dcp_desktop_phy_off, dcp->phy); + if (ret) + return ret; + + dcp->hdmi_pwren = devm_gpiod_get(dev, "hdmi-pwren", GPIOD_OUT_HIGH); + if (IS_ERR(dcp->hdmi_pwren)) + return dev_err_probe(dev, PTR_ERR(dcp->hdmi_pwren), + "HDMI power GPIO unavailable\n"); + dcp->dp2hdmi_pwren = devm_gpiod_get(dev, "dp2hdmi-pwren", GPIOD_OUT_HIGH); + if (IS_ERR(dcp->dp2hdmi_pwren)) + return dev_err_probe(dev, PTR_ERR(dcp->dp2hdmi_pwren), + "DP2HDMI power GPIO unavailable\n"); + msleep(100); + dev_info(dev, "fixed HDMI PHY and converter rails prepared\n"); + return 0; +} + /* * An adopted T8132 dcpext offers its Type-C routes. It is no component of * the DRM device (its outputs are added by t8132_ext_drm.c): the routes @@ -6581,8 +6629,18 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev) return ret; if (iomfb_is_m4(dcp)) { - dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP; - dcp->connector_type = DRM_MODE_CONNECTOR_eDP; + bool main_external = dcp_t8132_main_external(dev->of_node); + + if (main_external) { + dcp->fixed_connector_type = DRM_MODE_CONNECTOR_HDMIA; + dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA; + ret = dcp_m4_hdmi_prepare(dcp); + if (ret) + return ret; + } else { + dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP; + dcp->connector_type = DRM_MODE_CONNECTOR_eDP; + } bitmap_zero(dcp->iomfb_surfaces, DCP_MAX_PLANES); set_bit(0, dcp->iomfb_surfaces); ret = iomfb_m4_init(dcp); @@ -6592,11 +6650,20 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev) * An external DCP is no component of the DRM device. Once * adoption starts, its session outlives the device. */ - if (of_device_is_compatible(dev->of_node, "apple,t8132-dcpext")) { + if (of_device_is_compatible(dev->of_node, "apple,t8132-dcpext") || + main_external) { ret = dcp_pin_live_session(dcp); if (!ret) ret = iomfb_m4_adopt_external(dcp); - if (!ret) + if (!ret && main_external) { + ret = dcp_desktop_dptx_connect(dcp, 0); + if (ret) { + dev_warn(dev, "initial HDMI DPTX connect failed: %d\n", + ret); + ret = 0; + } + } + if (!ret && !main_external) dcp_t8132_ext_routes(dcp); return ret; } @@ -7079,7 +7146,8 @@ static void dcp_platform_remove(struct platform_device *pdev) if (dcp) av_audiosrv_quiesce(dcp); /* An adopted external DCP never joined the DRM device. */ - if (of_device_is_compatible(pdev->dev.of_node, "apple,t8132-dcpext")) { + if (of_device_is_compatible(pdev->dev.of_node, "apple,t8132-dcpext") || + dcp_t8132_main_external(pdev->dev.of_node)) { /* Enabled with its Type-C routes, see dcp_t8132_ext_routes(). */ if (dcp) disable_delayed_work_sync(&dcp->typec_reconnect_wq); diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index f47196a535e8f..e64c388aeda7d 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -15,6 +15,7 @@ bool dcp_h17p_external_supported(struct device_node *np); bool dcp_h17p_external_ready(struct device_node *np); +bool dcp_t8132_main_external(struct device_node *np); struct apple_atomic_state { struct drm_atomic_commit base; diff --git a/drivers/gpu/drm/apple/iomfb_m4.c b/drivers/gpu/drm/apple/iomfb_m4.c index 3ae666b093ea7..e2ddcfae78139 100644 --- a/drivers/gpu/drm/apple/iomfb_m4.c +++ b/drivers/gpu/drm/apple/iomfb_m4.c @@ -2635,9 +2635,11 @@ int iomfb_m4_init(struct apple_dcp *owner) u32 ready = 0; int ret; /* An external DCP the boot loader handed over: same firmware, no panel. */ - bool external = of_device_is_compatible(dev->of_node, "apple,t8132-dcpext"); + bool dcpext = of_device_is_compatible(dev->of_node, "apple,t8132-dcpext"); + bool main_external = dcp_t8132_main_external(dev->of_node); + bool external = dcpext || main_external; - if (external && + if (dcpext && (of_property_read_u32(dev->of_node, "apple,t8132-dcpext-firmware-ready", &ready) || ready != 1)) return dev_err_probe(dev, -ENODEV, "external DCP not handed over\n"); @@ -2657,10 +2659,14 @@ int iomfb_m4_init(struct apple_dcp *owner) if (ret) return ret; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "coproc"); - if (!res || resource_size(res) != SZ_16K || - (external ? res->start != 0x286e00000ULL && res->start != 0x48ae00000ULL : + if (!res) + return dev_err_probe(dev, -ENODEV, "missing coproc resource\n"); + if (resource_size(res) != SZ_16K || + (dcpext ? res->start != 0x286e00000ULL && res->start != 0x48ae00000ULL : res->start != 0x30ae00000ULL)) - return -ENODEV; + return dev_err_probe(dev, -ENODEV, + "unqualified %s DCP coproc resource %pa-%pa\n", + dcpext ? "external" : "main", &res->start, &res->end); /* Resolve the fallible supplier before creating a mailbox owner. */ ret = external ? 0 : apple_pmp_j713_display_prepare(dev); if (ret) diff --git a/drivers/gpu/drm/apple/t8132_ext_drm.c b/drivers/gpu/drm/apple/t8132_ext_drm.c index 911f011ae400b..0c219f775ef81 100644 --- a/drivers/gpu/drm/apple/t8132_ext_drm.c +++ b/drivers/gpu/drm/apple/t8132_ext_drm.c @@ -48,6 +48,7 @@ #include #include +#include "dcp.h" #include "iomfb_m4.h" #include "t8132_ext_drm.h" @@ -1044,7 +1045,8 @@ static int t8132_ext_out_init(struct drm_device *drm, struct platform_device *pd goto err_crtc; out->encoder.possible_crtcs = drm_crtc_mask(&out->crtc); ret = drm_connector_init(drm, &out->connector, &t8132_ext_connector_funcs, - DRM_MODE_CONNECTOR_DisplayPort); + dcp_t8132_main_external(pdev->dev.of_node) ? + DRM_MODE_CONNECTOR_HDMIA : DRM_MODE_CONNECTOR_DisplayPort); if (ret) goto err_encoder; drm_connector_helper_add(&out->connector, &t8132_ext_connector_helpers); @@ -1121,6 +1123,31 @@ int t8132_ext_drm_init(struct drm_device *drm) put_device(&pdev->dev); ret = ret ?: err; } + /* A main DCP can describe a fixed external output instead of a panel. */ + if (count < T8132_EXT_MAX) { + struct platform_device *pdev; + + np = of_find_compatible_node(NULL, NULL, "apple,t8132-dcp"); + if (dcp_t8132_main_external(np) && of_device_is_available(np)) { + pdev = of_find_device_by_node(np); + if (pdev) { + if (!device_is_bound(&pdev->dev)) { + ret = ret ?: -EPROBE_DEFER; + } else { + err = t8132_ext_out_init(drm, pdev, mine); + if (err) + dev_err(&pdev->dev, + "no main external DRM output: %d\n", + err); + else + count++; + ret = ret ?: err; + } + put_device(&pdev->dev); + } + } + of_node_put(np); + } /* * After the outputs, not under the registry lock (see there); * unregistered first by t8132_ext_drm_fini(). diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py index 86a95e0f4178e..21d05585d6147 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py @@ -1450,6 +1450,7 @@ def function(source, marker): + function(drm, 'struct apple_dcp_start_work {') + ';\n' + function(drm, 'static void apple_dcp_start_fn(')) if 'static void apple_dcp_start_fn(' in drm else '', + function(drm, 'static bool apple_drm_has_output('), function(drm, 'static int apple_drm_init_dcp('), 'static int publish_provider(struct platform_device *pdev, struct apple_dcp *dcp)\n' '{\n\tstruct device *dev = &pdev->dev;\n\tint ret;\n' @@ -1494,6 +1495,11 @@ def function(source, marker): struct { int iomfb_method_profile; } hw; struct work_struct external_audio_work; }; +/* This J700 fixture has neither a T8132 main DCP nor its fixed HDMI path. */ +static bool __attribute__((unused)) dcp_t8132_main_external(struct device_node *n) +{ (void)n; return false; } +static bool __attribute__((unused)) apple_t8132_main_external_present(void) +{ return false; } /* This readiness fixture has no AV owner or native audio initializer. */ static void av_audiosrv_quiesce(struct apple_dcp *d) { assert(!d->external_native); } @@ -1516,6 +1522,8 @@ def function(source, marker): static void __attribute__((unused)) iomfb_v14_7_external_refuse(struct apple_dcp *d, const char *why) { (void)d; (void)why; assert(!"T6030 external processor in a J700 scenario"); } struct drm_device { int unused; }; +struct drm_crtc { int unused; }; +#define drm_for_each_crtc(crtc, drm) for ((void)(drm), (crtc) = NULL; (crtc); ) struct drm_encoder { int unused; }; /* Not a J713: production registers no T8132 external outputs and returns 0. */ static int __attribute__((unused)) t8132_ext_drm_init(struct drm_device *drm) { (void)drm; return 0; } From 39d81c9ac9a19fa82cfa2c57febcde1e2c5b724a Mon Sep 17 00:00:00 2001 From: Niklas Sheth Date: Tue, 18 Aug 2026 09:57:36 +0000 Subject: [PATCH 3/4] phy: apple: dptx: add the T8122 PHY T8122 (as used on T8132) needs its own cold activation: the T8112 core power steps, moved enable/clear bits (0x2218, 0x2234), a different 0x2230 setting, no 0x4008/0x420c/0x4404/0x4408/0x4600 steps, and an AUX impedance calibration whose result is copied into 0x4228. At link-rate change it clears 0x222c bit 1 instead of 0x2220 bit 7, takes the T6020 settings for 0x20b0, the first 0x20b4 write and 0x20b8 bit 16, then clears 0x20b4 bit 1 and skips the 0x20b8 bit 21 clear. For T8122 only, enable runtime PM before creating the PHY so the generic PHY core propagates runtime-PM references to the provider. The T8112, T6020 and T6050 paths are unchanged. Link: https://github.com/GravityLinux/linux/commit/877c574dfa4f31434129b05da6d907abfb21bbef Assisted-By: OpenAI Codex (GPT-5.6 Sol) Signed-off-by: Niklas Sheth [Sean: extracted the T8122 hunk; rebased over T6050; runtime PM for T8122 only; calibration code from the readback] Assisted-by: LLM checkpatch Signed-off-by: Sean Dolbec --- drivers/phy/apple/dptx.c | 118 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/drivers/phy/apple/dptx.c b/drivers/phy/apple/dptx.c index f191efe4c6286..b48f12df3f282 100644 --- a/drivers/phy/apple/dptx.c +++ b/drivers/phy/apple/dptx.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ enum apple_dptx_type { DPTX_PHY_T8112, DPTX_PHY_T6020, DPTX_PHY_T6050, + DPTX_PHY_T8122, }; /* The PLL core calibration the board describes, in t6050_pll_core_cfg() order. */ @@ -150,6 +152,82 @@ static int dptx_phy_set_active_lane_count(struct apple_dptx_phy *phy, u32 num_la return 0; } +static int dptx_phy_activate_t8122(struct apple_dptx_phy *phy, u32 dcp_index) +{ + u32 done, code, val; + + dev_dbg(phy->dev, "T8122 PHY activation: begin (DCP %u)\n", + dcp_index); + + /* Firmware raises the private PHY gate immediately before this callback. */ + usleep_range(5000, 6000); + + readl(phy->regs.core + 0x10); + writel(dcp_index, phy->regs.core + 0x10); + + set32(phy->regs.core + 0x48, 0x010); + set32(phy->regs.core + 0x48, 0x020); + clear32(phy->regs.core + 0x48, 0x040); + set32(phy->regs.core + 0x48, 0x100); + set32(phy->regs.core + 0x48, 0x200); + clear32(phy->regs.core + 0x48, 0x400); + set32(phy->regs.core + 0x48, 0x001); + set32(phy->regs.core + 0x48, 0x002); + clear32(phy->regs.core + 0x48, 0x004); + + val = readl(phy->regs.dptx + 0x2014); + writel((val & ~0x3f0000) | (0x30 << 16), + phy->regs.dptx + 0x2014); + set32(phy->regs.dptx + 0x20b8, 0x010000); + clear32(phy->regs.dptx + 0x2220, 0x0000002); + set32(phy->regs.dptx + 0x222c, 0x000800); + set32(phy->regs.dptx + 0x222c, 0x000100); + set32(phy->regs.dptx + 0x2230, 0x0200000); + clear32(phy->regs.dptx + 0x2278, 0x08000000); + set32(phy->regs.dptx + 0x22a4, 0x0000001); + + for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; + loff += DPTX_LANE_STRIDE) { + val = readl(phy->regs.dptx + loff + 0x40); + writel((val & ~0xe00000) | 0x200000, + phy->regs.dptx + loff + 0x40); + } + for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; + loff += DPTX_LANE_STRIDE) + set32(phy->regs.dptx + loff + 0x40, 0x080000); + for (u32 loff = DPTX_LANE0_OFFSET; loff < DPTX_LANE_END; + loff += DPTX_LANE_STRIDE) + clear32(phy->regs.dptx + loff + 0x244, 0x10); + + set32(phy->regs.dptx + 0x2218, 0x001); + clear32(phy->regs.dptx + 0x2234, 0x00000001); + set32(phy->regs.dptx + 0x2200, 0x0002); + clear32(phy->regs.dptx + 0x1000, 0x00000002); + set32(phy->regs.dptx + 0x4004, 0x08); + + dptx_phy_set_active_lane_count(phy, 0); + + dev_dbg(phy->dev, "T8122 PHY activation: calibration start\n"); + set32(phy->regs.dptx + 0x4224, 1); + set32(phy->regs.dptx + 0x4200, 0x3c0); + set32(phy->regs.dptx + 0x4200, 1); + udelay(125); + done = readl(phy->regs.dptx + 0x4204) & 1; + clear32(phy->regs.dptx + 0x4200, 1); + code = (readl(phy->regs.dptx + 0x4210) >> 18) & 0x7f; + if (!done) { + dev_err(phy->dev, "impedance calibration did not complete (0x4204 %#x)\n", + readl(phy->regs.dptx + 0x4204)); + return -ETIMEDOUT; + } + mask32(phy->regs.dptx + 0x4228, 0x7f0000, code << 16); + set32(phy->regs.dptx + 0x4228, 0x8000); + dev_dbg(phy->dev, "T8122 PHY activation: complete, calibration code %#x\n", + code); + + return 0; +} + static int dptx_phy_activate(struct apple_dptx_phy *phy, u32 dcp_index) { u32 val_2014; @@ -157,6 +235,8 @@ static int dptx_phy_activate(struct apple_dptx_phy *phy, u32 dcp_index) u32 val_4408; dev_dbg(phy->dev, "activate(dcp:%u)\n", dcp_index); + if (phy->hw.type == DPTX_PHY_T8122) + return dptx_phy_activate_t8122(phy, dcp_index); // MMIO: R.4 0x23c500010 (dptx-phy[1], offset 0x10) = 0x0 // MMIO: W.4 0x23c500010 (dptx-phy[1], offset 0x10) = 0x0 @@ -406,13 +486,17 @@ static int dptx_phy_set_link_rate(struct apple_dptx_phy *phy, u32 link_rate) // MMIO: R.4 0x23c542220 (dptx-phy[0], offset 0x2220) = 0x11090a0 // MMIO: W.4 0x23c542220 (dptx-phy[0], offset 0x2220) = 0x1109020 - clear32(phy->regs.dptx + 0x2220, 0x0000080); + if (phy->hw.type == DPTX_PHY_T8122) + clear32(phy->regs.dptx + 0x222c, 0x0000002); + else + clear32(phy->regs.dptx + 0x2220, 0x0000080); // MMIO: R.4 0x23c5420b0 (dptx-phy[0], offset 0x20b0) = 0x1e0e01c2 // MMIO: W.4 0x23c5420b0 (dptx-phy[0], offset 0x20b0) = 0x1e0e01c2 val_20b0 = readl(phy->regs.dptx + 0x20b0); /* TODO: what happens on dptx-phy */ - if (phy->hw.type == DPTX_PHY_T6020) + if (phy->hw.type == DPTX_PHY_T6020 || + phy->hw.type == DPTX_PHY_T8122) val_20b0 = (val_20b0 & ~0x3ff) | 0x2a3; writel(val_20b0, phy->regs.dptx + 0x20b0); @@ -420,7 +504,8 @@ static int dptx_phy_set_link_rate(struct apple_dptx_phy *phy, u32 link_rate) // MMIO: W.4 0x23c5420b4 (dptx-phy[0], offset 0x20b4) = 0x7fffffe val_20b4 = readl(phy->regs.dptx + 0x20b4); /* TODO: what happens on dptx-phy */ - if (phy->hw.type == DPTX_PHY_T6020) + if (phy->hw.type == DPTX_PHY_T6020 || + phy->hw.type == DPTX_PHY_T8122) val_20b4 = (val_20b4 | 0x4000000) & ~0x0008000; writel(val_20b4, phy->regs.dptx + 0x20b4); @@ -428,7 +513,9 @@ static int dptx_phy_set_link_rate(struct apple_dptx_phy *phy, u32 link_rate) // MMIO: W.4 0x23c5420b4 (dptx-phy[0], offset 0x20b4) = 0x7fffffe val_20b4 = readl(phy->regs.dptx + 0x20b4); /* TODO: what happens on dptx-phy */ - if (phy->hw.type == DPTX_PHY_T6020) + if (phy->hw.type == DPTX_PHY_T8122) + val_20b4 &= ~0x0000002; + else if (phy->hw.type == DPTX_PHY_T6020) val_20b4 = (val_20b4 | 0x0000001) & ~0x0000004; writel(val_20b4, phy->regs.dptx + 0x20b4); @@ -443,13 +530,15 @@ static int dptx_phy_set_link_rate(struct apple_dptx_phy *phy, u32 link_rate) // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 /* TODO: unclear */ - if (phy->hw.type == DPTX_PHY_T6020) + if (phy->hw.type == DPTX_PHY_T6020 || + phy->hw.type == DPTX_PHY_T8122) set32(phy->regs.dptx + 0x20b8, 0x010000); else set32(phy->regs.dptx + 0x20b8, 0); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x654800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x454800 - clear32(phy->regs.dptx + 0x20b8, 0x200000); + if (phy->hw.type != DPTX_PHY_T8122) + clear32(phy->regs.dptx + 0x20b8, 0x200000); // MMIO: R.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x454800 // MMIO: W.4 0x23c5420b8 (dptx-phy[0], offset 0x20b8) = 0x454800 @@ -1049,6 +1138,7 @@ static int dptx_phy_probe(struct platform_device *pdev) { struct apple_dptx_phy *dptx_phy; struct device *dev = &pdev->dev; + int ret; dptx_phy = devm_kzalloc(dev, sizeof(*dptx_phy), GFP_KERNEL); if (!dptx_phy) @@ -1061,6 +1151,17 @@ static int dptx_phy_probe(struct platform_device *pdev) mutex_init(&dptx_phy->lock); + /* + * The generic PHY core only propagates runtime-PM references to the + * provider when runtime PM was enabled before devm_phy_create(). + */ + if (dptx_phy->hw.type == DPTX_PHY_T8122) { + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable runtime PM\n"); + } + dptx_phy->regs.core = devm_platform_ioremap_resource_byname(pdev, "core"); if (IS_ERR(dptx_phy->regs.core)) @@ -1099,10 +1200,15 @@ static const struct apple_dptx_phy_hw apple_dptx_hw_t6050 = { .type = DPTX_PHY_T6050, }; +static const struct apple_dptx_phy_hw apple_dptx_hw_t8122 = { + .type = DPTX_PHY_T8122, +}; + static const struct of_device_id dptx_phy_match[] = { { .compatible = "apple,t6020-dptx-phy", .data = &apple_dptx_hw_t6020 }, { .compatible = "apple,t8112-dptx-phy", .data = &apple_dptx_hw_t8112 }, { .compatible = "apple,t6050-dptx-phy", .data = &apple_dptx_hw_t6050 }, + { .compatible = "apple,t8122-dptx-phy", .data = &apple_dptx_hw_t8122 }, {}, }; MODULE_DEVICE_TABLE(of, dptx_phy_match); From 36383187409aa16e0688dd4bec56f0bc72e5b98d Mon Sep 17 00:00:00 2001 From: Sean Dolbec Date: Sat, 7 Nov 2026 20:07:55 -0500 Subject: [PATCH 4/4] arm64: dts: apple: t8132-j773g: enable HDMI display The Mac mini routes its main DCP through the T8122 DPTX PHY and an HDMI converter controlled by two SMC GPIOs. Describe that fixed path and enable the retained DCP, mailbox, and display DART nodes. Do not attach the DCP or mailbox to DISP_CPU: iBoot leaves that domain in auto mode and direct PMGR writes fault on this machine. Assisted-by: LLM checkpatch Signed-off-by: Sean Dolbec --- arch/arm64/boot/dts/apple/t8132-j773g.dts | 51 +++++++++++++++++++---- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8132-j773g.dts b/arch/arm64/boot/dts/apple/t8132-j773g.dts index baf3e3ffcfecb..88632439108b5 100644 --- a/arch/arm64/boot/dts/apple/t8132-j773g.dts +++ b/arch/arm64/boot/dts/apple/t8132-j773g.dts @@ -34,13 +34,50 @@ }; }; -/* - * iBoot scans out through dispext0 to the HDMI port (dptx-phy and a - * DP-to-HDMI converter at i2c0 0x08, hot-plug on AOP GPIO 41): keep the - * power domains used for it on. - */ -&framebuffer0 { - power-domains = <&ps_dispext0_fe>, <&ps_dptx_phy>; +&soc { + dptx_phy: phy@39a700000 { + compatible = "apple,t8122-dptx-phy", "apple,dptx-phy"; + reg = <0x3 0x9a700000 0x0 0x4000>, + <0x3 0x9a740000 0x0 0x10000>; + reg-names = "core", "dptx"; + power-domains = <&ps_dptx_phy>; + #phy-cells = <0>; + status = "okay"; + }; +}; + +&dcp_mbox { + /* iBoot leaves DISP_CPU in auto mode; Linux must not switch it. */ + /delete-property/ power-domains; + status = "okay"; +}; + +&dcp_dart { + status = "okay"; +}; + +&disp0_dart { + status = "okay"; +}; + +&dcp { + /* iBoot leaves DISP_CPU in auto mode; Linux must not switch it. */ + /delete-property/ power-domains; + apple,connector-type = "HDMI-A"; + apple,dptx-phy = <4>; + phys = <&dptx_phy>; + phy-names = "dp-phy"; + hdmi-pwren-gpios = <&smc_gpio 15 GPIO_ACTIVE_HIGH>; + dp2hdmi-pwren-gpios = <&smc_gpio 26 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&disp0_piodma { + status = "okay"; +}; + +&display { + status = "okay"; }; /*