diff --git a/arch/arm64/boot/dts/apple/t8132-j773g.dts b/arch/arm64/boot/dts/apple/t8132-j773g.dts index baf3e3ffcfec..88632439108b 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"; }; /* diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 5e2524241949..3b8a1b8da90b 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 438f734f87d0..54952950e194 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 e9aaf1f1884f..fbb7d7881623 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 f47196a535e8..e64c388aeda7 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 3ae666b093ea..e2ddcfae7813 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 911f011ae400..0c219f775ef8 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/drivers/iommu/apple-dart-dcp-format.h b/drivers/iommu/apple-dart-dcp-format.h index 52b853b59d97..a2344756b211 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 2396ac5e1d33..94b01309ab16 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 49ed48c2f3de..2da29fd1ad65 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 7cf99361bf79..40ceb6ffd053 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: diff --git a/drivers/phy/apple/dptx.c b/drivers/phy/apple/dptx.c index f191efe4c628..b48f12df3f28 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); diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py index 86a95e0f4178..21d05585d614 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; }