diff --git a/Documentation/arch/arm64/index.rst b/Documentation/arch/arm64/index.rst index 061e2f5e50253..4b206ea9a6d29 100644 --- a/Documentation/arch/arm64/index.rst +++ b/Documentation/arch/arm64/index.rst @@ -9,10 +9,11 @@ ARM64 Architecture acpi_object_usage amu - aurora-apif - aurora-apif-nvme - aurora-apif-pcie - aurora-apif-pmp + ../../driver-api/firmware/aurora-apif + ../../driver-api/firmware/aurora-apif-nvme + ../../driver-api/firmware/aurora-apif-pcie + ../../driver-api/firmware/aurora-apif-pmp + ../../driver-api/firmware/aurora-apif-display aurora-apif-centauri apple-j813 arm-acpi diff --git a/Documentation/devicetree/bindings/display/apple,dcp.yaml b/Documentation/devicetree/bindings/display/apple,dcp.yaml index bba5d64f50451..f03bbb5837c5a 100644 --- a/Documentation/devicetree/bindings/display/apple,dcp.yaml +++ b/Documentation/devicetree/bindings/display/apple,dcp.yaml @@ -25,6 +25,7 @@ properties: - apple,t6020-dcp - apple,t8140-dcp - apple,t8142-dcp + - apple,t6050-dcp - const: apple,dcp - items: - enum: @@ -32,6 +33,7 @@ properties: - apple,t8112-dcpext - apple,t6000-dcpext - apple,t6020-dcpext + - apple,t6050-dcpext - const: apple,dcpext - items: - const: apple,t8140-dcpext @@ -70,13 +72,32 @@ properties: clocks: minItems: 1 - maxItems: 2 + maxItems: 64 clock-names: minItems: 1 - items: - - const: clock-154 - - const: clock-194 + maxItems: 64 + + apple,clock-ids: + oneOf: + - type: boolean + - $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 64 + description: | + Ordered firmware provider clock identifiers. Callback clock indices + select the corresponding entry in clocks. An explicitly empty table + is allowed when the provider has no clocks. + + apple,firmware-physical-regs: + $ref: /schemas/types.yaml#/definitions/uint64-array + minItems: 1 + maxItems: 10 + description: | + Coprocessor physical base addresses in display register-table order, + including the bandwidth scratch and optional doorbell windows. + The number of entries must match the complete register table. + Omit on identity-mapped systems; reg supplies the physical view. memory-region: minItems: 1 @@ -315,12 +336,39 @@ required: - apple,bw-scratch allOf: + - if: + properties: + compatible: + contains: + enum: ['apple,t6050-dcp', 'apple,t6050-dcpext'] + then: + required: + - apple,clock-ids + - memory-region + - memory-region-names + - apple,firmware-compat + properties: + apple,firmware-compat: + items: + - const: 27 + - const: 0 + - const: 0 + else: + properties: + clocks: + maxItems: 2 + clock-names: + items: + - const: clock-154 + - const: clock-194 + apple,clock-ids: false + apple,firmware-physical-regs: false - if: not: properties: compatible: contains: - const: apple,t8140-dcp + enum: ['apple,t8140-dcp', 'apple,t6050-dcp', 'apple,t6050-dcpext'] then: properties: apple,power-lut-data-x: false @@ -332,7 +380,8 @@ allOf: properties: compatible: contains: - enum: ['apple,t8140-dcp', 'apple,t8140-dcpext'] + enum: ['apple,t8140-dcp', 'apple,t8140-dcpext', + 'apple,t6050-dcp', 'apple,t6050-dcpext'] then: properties: resets: false diff --git a/Documentation/driver-api/firmware/aurora-apif-display.rst b/Documentation/driver-api/firmware/aurora-apif-display.rst new file mode 100644 index 0000000000000..cbef7c806a42a --- /dev/null +++ b/Documentation/driver-api/firmware/aurora-apif-display.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +DCP display in an Aura guest +=========================== + +The firmware-27 display profile uses the normal IOMFB shared-memory +transport and compact AFK framing. It supplies a separate method/callback +table and present serializer. The existing queued present, framebuffer +retention, backlight policy, Type-C routing and suspend drain are shared +with the public driver. + +This is display-controller support. It does not add AGX acceleration. + +Loader contract +--------------- + +The loader leaves each coprocessor running with its firmware translations +intact. The driver negotiates RTKit without writing its CPU run state or +restarting the coprocessor. A missing running session or a firmware ABI +other than the explicit apple,firmware-compat = <27 0 0> is rejected. + +The internal and external providers use apple,t6050-dcp and +apple,t6050-dcpext respectively. The profile uses a 41-bit DMA +aperture for the coprocessor CPU; scanout and PIODMA retain their independent apertures. + +Each provider describes its inherited framebuffer region using +memory-region and the framebuffer entry in memory-region-names. +Default-surface allocation uses firmware-reported geometry, format and +compression. Unsupported layouts or allocations larger than that region +are rejected. + +apple,clock-ids describes the ordered firmware provider clock table. +Its entry count must match clocks. An explicitly empty property is +accepted for a provider with no clocks; a missing property is rejected. +A callback clock index selects the corresponding Linux clock. + +Display register callbacks normally use resource physical addresses. +If Linux resources are translated guest addresses, the loader supplies +apple,firmware-physical-regs in complete register-table order, +including the bandwidth scratch and optional doorbell apertures. These +addresses are returned to the coprocessor, not dereferenced by Linux. + +The loader must also grant the reserved firmware buffers needed by AFK +through the corresponding IOMMU provider. Remote requests remain bounded +and fail if their device addresses cannot be translated. + +Direct DisplayPort routes +------------------------- + +Protocol-27 DPTX uses endpoint 0x2c and its own drive-settings records. +The explicit service unit takes precedence over the transport interface +identifier. Drive requests validate lane count and voltage/pre-emphasis +before changing the PHY. + +The PHY reports its live clock and coding mode to the crossbar consumer. +A missing required provider or conflicting clock configuration fails the +link operation. Other PHY types retain their existing routing behavior. + +Session lifetime +---------------- + +An adopted session has no confirmed DMA stop boundary. Failed probes and +unbind therefore retain published buffers, mappings and device/domain +references until a fresh firmware boot. Rebinding a retained session is +unsupported. No timeout falls back to resetting a live coprocessor. + +Validation +---------- + +Native arm64 display-driver builds and host tests cover the present wire +layout and framebuffer sizing/bounds. These checks do not establish boot, +physical hotplug, brightness or suspend behavior on hardware. diff --git a/arch/arm64/configs/aurora-apif.config b/arch/arm64/configs/aurora-apif.config index 5282922ee519a..148579360934a 100644 --- a/arch/arm64/configs/aurora-apif.config +++ b/arch/arm64/configs/aurora-apif.config @@ -41,3 +41,6 @@ CONFIG_APPLE_CENTAURI=y CONFIG_APPLE_CENTAURI_WIFI=y CONFIG_BT=y CONFIG_BT_HCICENTAURI=y +CONFIG_DRM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_DRM_APPLE=y diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index 837aa713ad435..8e64394e7fa81 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -10,7 +10,7 @@ appledrm-y += connector.o appledrm-y += ibootep.o appledrm-y += iomfb_v12_3.o appledrm-y += iomfb_v13_3.o -appledrm-y += iomfb_h17p.o +appledrm-y += iomfb_h17p.o iomfb_v27.o appledrm-$(CONFIG_DRM_APPLE_KUNIT_TEST) += iomfb_h17p_test.o appledrm-y += epic/dpavservep.o appledrm-y += plane.o diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 84427172edc51..7f1d0c15106a4 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -342,7 +342,7 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, return; } ret = parse_epic_service_init(&ctx, &epic_name, &epic_class, &epic_unit, - ep->dcp->fw_compat == DCP_FIRMWARE_H17P); + ep->dcp->fw_compat >= DCP_FIRMWARE_H17P); if (ret) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: failed to extract init props: %d\n", diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index c3e9e8b9f66fe..0da68d8e5d8fa 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -20,6 +20,7 @@ #include "dcp_backlight.h" #include "iomfb.h" #include "iomfb_h17p.h" +#include "iomfb_v27.h" #include "iomfb-state.h" #include "iomfb_v12_3.h" #include "iomfb_v13_3.h" @@ -69,6 +70,7 @@ struct apple_dcp_typec_route { }; bool dcp_is_typec_output(struct apple_dcp *dcp); +int dcp_direct_crossbar_link(struct apple_dcp *dcp, bool up); void dcp_swap_watchdog_arm(struct apple_dcp *dcp); void dcp_swap_watchdog_complete(struct apple_dcp *dcp); bool dcp_is_usb4_output(struct apple_dcp *dcp); @@ -93,6 +95,7 @@ enum { DPAVSERV_ENDPOINT = 0x28, AV_ENDPOINT = 0x29, DPTX_ENDPOINT = 0x2a, + DPTX_ENDPOINT_V27 = 0x2c, HDCP_ENDPOINT = 0x2b, REMOTE_ALLOC_ENDPOINT = 0x2d, IOMFB_ENDPOINT = 0x37, @@ -178,6 +181,7 @@ enum dcp_iomfb_method_profile { }; struct apple_dcp_hw_data { + u8 firmware_dma_bits; u32 num_dptx_ports; enum dcp_iomfb_method_profile iomfb_method_profile; /* @@ -229,6 +233,16 @@ struct apple_dcp { /* clock rate request by dcp in */ struct clk *clk; struct clk *clk_194; + struct clk_bulk_data *firmware_clocks; + unsigned int num_firmware_clocks; + u64 disp_firmware_base[MAX_DISP_REGISTERS]; + u32 dfb_format, dfb_compression; + u64 dfb_region_size; + bool dfb_format_valid, dfb_compression_valid; + struct list_head iomfb_boolean_properties; + u32 iomfb_boolean_property_count; + u32 external_pipes_v27, external_displays_v27, external_suppression_v27; + struct dcp_swap_submit_req_v27 present_v27; /* DCP shared memory */ void *shmem; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 2096f86022e09..c288a2d3106bc 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -80,7 +80,7 @@ static int dcp_pin_live_session(struct apple_dcp *dcp) { struct dcp_session_ref *session; - if (!dcp->hw.adopt_live_session || dcp->fw_compat != DCP_FIRMWARE_H17P || + if (!dcp->hw.adopt_live_session || dcp->fw_compat < DCP_FIRMWARE_H17P || dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G || dcp->retain_dma) return 0; session = kzalloc_obj(*session); @@ -1126,6 +1126,38 @@ static void dcp_tunnel_prepare(struct apple_dcp_typec_route *route, dev_warn(dcp->dev, "DP tunnel PHY open failed: %d\n", ret); } +int dcp_direct_crossbar_link(struct apple_dcp *dcp, bool up) +{ + typeof(&apple_dp_phy_link_config) get_config; + typeof(&apple_dpxbar_link_configure) configure; + unsigned int pclk; + bool uhbr; + int ret; + + guard(mutex)(&dcp->tb_lock); + if (dcp->dptx_tunnel || !dcp->phy || !dcp->xbar) + return 0; + get_config = symbol_get(apple_dp_phy_link_config); + if (!get_config) + return dcp->fw_compat == DCP_FIRMWARE_V_27_0 ? -ENODEV : 0; + ret = get_config(dcp->phy, &pclk, &uhbr); + symbol_put(apple_dp_phy_link_config); + if (ret == -EOPNOTSUPP) + return 0; + if (!up && ret == -ENOLINK) + ret = 0; + if (ret) + return ret; + if (!up) + return dcp_dpxbar_link(dcp->xbar, false); + configure = symbol_get(apple_dpxbar_link_configure); + if (!configure) + return -ENODEV; + ret = configure(dcp->xbar, pclk, uhbr); + symbol_put(apple_dpxbar_link_configure); + return ret ? ret : dcp_dpxbar_link(dcp->xbar, true); +} + /* DP IN adapter handshake through the thunderbolt glue; tb_lock held */ static int dcp_tunnel_dpin_locked(struct apple_dcp *dcp, bool active) { @@ -1865,6 +1897,7 @@ static struct apple_dcp_afkep *dcp_afkep(struct apple_dcp *dcp, u8 endpoint) return dcp->ibootep; case DPAVSERV_ENDPOINT: return dcp->dcpavservep; + case DPTX_ENDPOINT_V27: case DPTX_ENDPOINT: return dcp->dptxep; default: @@ -1925,7 +1958,7 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) * remote allocator endpoint, for one). Log their messages rather than * dereferencing a NULL afkep or warning about an unknown endpoint. */ - if (dcp->fw_compat == DCP_FIRMWARE_H17P && endpoint != IOMFB_ENDPOINT && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && endpoint != IOMFB_ENDPOINT && !dcp_afkep(dcp, endpoint)) { dev_dbg_ratelimited(dcp->dev, "ep %#04x: %#llx\n", endpoint, message); @@ -1947,6 +1980,7 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) case DPAVSERV_ENDPOINT: ep = dcp->dcpavservep; break; + case DPTX_ENDPOINT_V27: case DPTX_ENDPOINT: ep = dcp->dptxep; break; @@ -1974,7 +2008,7 @@ static void dcp_rtk_crashed(void *cookie, const void *crashlog, size_t crashlog_ dcp->crashed = true; dev_err(dcp->dev, "DCP has crashed\n"); - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) { /* No in-place restart exists; the last scanout stays retained. */ dev_err(dcp->dev, "a reboot is required to restore the display\n"); @@ -2542,7 +2576,9 @@ int dcp_start(struct platform_device *pdev) ret); } - if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { + if ((dcp->phy || (dcp->fw_compat == DCP_FIRMWARE_V_27_0 && + dcp->hw.num_dptx_ports)) && + dcp->fw_compat >= DCP_FIRMWARE_V_13_5) { ret = ibootep_init(dcp); if (ret) dev_warn(dcp->dev, "Failed to start IBOOT endpoint: %d\n", @@ -2607,6 +2643,9 @@ static void _dcp_poweroff(struct apple_dcp *dcp) case DCP_FIRMWARE_H17P: iomfb_poweroff_h17p(dcp); break; + case DCP_FIRMWARE_V_27_0: + iomfb_poweroff_v27(dcp); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); break; @@ -2672,6 +2711,9 @@ static void __maybe_unused dcp_sleep(struct apple_dcp *dcp) case DCP_FIRMWARE_H17P: iomfb_sleep_h17p(dcp); break; + case DCP_FIRMWARE_V_27_0: + iomfb_sleep_v27(dcp); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); break; @@ -2683,7 +2725,7 @@ static void __maybe_unused dcp_sleep(struct apple_dcp *dcp) static bool dcp_uses_soft_dpms(struct apple_dcp *dcp) { return (dcp_backlight_active(dcp) && dcp_has_panel(dcp)) || - (dcp->fw_compat == DCP_FIRMWARE_H17P && + (dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G && dcp->connector_type == DRM_MODE_CONNECTOR_eDP); } @@ -2696,10 +2738,13 @@ void dcp_poweron(struct platform_device *pdev) int ret; if (dcp_uses_soft_dpms(dcp)) { - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G && !READ_ONCE(dcp->pipe_enabled_h17p)) { - iomfb_poweron_h17p(dcp); + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0) + iomfb_poweron_v27(dcp); + else + iomfb_poweron_h17p(dcp); if (!READ_ONCE(dcp->pipe_enabled_h17p)) { dev_err(dcp->dev, "initial display pipe enable failed\n"); WRITE_ONCE(dcp->crashed, true); @@ -2761,6 +2806,9 @@ void dcp_poweron(struct platform_device *pdev) case DCP_FIRMWARE_H17P: iomfb_poweron_h17p(dcp); break; + case DCP_FIRMWARE_V_27_0: + iomfb_poweron_v27(dcp); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); break; @@ -3193,6 +3241,23 @@ static int dcp_get_disp_regs(struct apple_dcp *dcp) } dcp->nr_disp_registers = count; + for (i = 0; i < count; i++) + dcp->disp_firmware_base[i] = dcp->disp_registers[i]->start; + if (of_property_present(dcp->dev->of_node, "apple,firmware-physical-regs")) { + if (of_property_count_u64_elems(dcp->dev->of_node, + "apple,firmware-physical-regs") != count) + return -EINVAL; + ret = of_property_read_u64_array(dcp->dev->of_node, + "apple,firmware-physical-regs", + dcp->disp_firmware_base, count); + if (ret) + return ret; + for (i = 0; i < count; i++) + if (!IS_ALIGNED(dcp->disp_firmware_base[i], 4) || + resource_size(dcp->disp_registers[i]) > + U64_MAX - dcp->disp_firmware_base[i]) + return -EINVAL; + } return 0; } @@ -3247,6 +3312,13 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) char fw_str[DCP_FW_VERSION_STR_LEN]; int ret; + if (hw->firmware_compat == DCP_FIRMWARE_V_27_0) { + ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); + if (ret < 0 || strcmp(compat_str, "27.0.0")) + return DCP_FIRMWARE_UNKNOWN; + return DCP_FIRMWARE_V_27_0; + } + /* * SoCs introduced with H17-generation firmware pin its interface; the * loader may not recognise their boot firmware version. @@ -3319,6 +3391,43 @@ static void dcp_enable_typec_work(struct apple_dcp *dcp) mod_delayed_work(system_freezable_wq, &dcp->typec_reconnect_wq, 0); } +static int dcp_firmware_metadata_v27(struct apple_dcp *dcp) +{ + struct device *dev = dcp->dev; + struct device_node *fb; + struct resource resource; + int clocks, index, ret; + + index = of_property_match_string(dev->of_node, "memory-region-names", + "framebuffer"); + if (index < 0) + return -EINVAL; + fb = of_parse_phandle(dev->of_node, "memory-region", index); + if (!fb) + return -EINVAL; + ret = of_address_to_resource(fb, 0, &resource); + of_node_put(fb); + if (ret) + return ret; + dcp->dfb_region_size = resource_size(&resource); + if (!dcp->dfb_region_size) + return -EINVAL; + + clocks = of_property_count_u32_elems(dev->of_node, "apple,clock-ids"); + if (clocks == -ENODATA && + of_property_present(dev->of_node, "apple,clock-ids")) + clocks = 0; + if (clocks < 0 || clocks > 64) + return -EINVAL; + ret = devm_clk_bulk_get_all(dev, &dcp->firmware_clocks); + if (ret < 0) + return ret; + if (ret != clocks) + return -EINVAL; + dcp->num_firmware_clocks = clocks; + return 0; +} + static int dcp_comp_bind(struct device *dev, struct device *main, void *data) { struct device_node *panel_np; @@ -3326,6 +3435,14 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) u32 cpu_ctrl; int ret; + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0) { + INIT_LIST_HEAD(&dcp->iomfb_boolean_properties); + ret = devm_add_action_or_reset(dev, + iomfb_firmware_state_cleanup_v27, dcp); + if (ret) + return ret; + } + if (READ_ONCE(dcp->quiescing)) return dev_err_probe(dev, -EBUSY, "Live DCP session cannot be rebound; reboot required\n"); @@ -3338,7 +3455,8 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) return dev_err_probe(dev, ret, "Previous scanout has not been stopped\n"); - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42)); + ret = dma_set_mask_and_coherent(dev, + DMA_BIT_MASK(dcp->hw.firmware_dma_bits ?: 42)); if (ret) return ret; @@ -3420,7 +3538,15 @@ static int dcp_comp_bind(struct device *dev, struct device *main, void *data) goto err_piodma; } - dcp->clk = devm_clk_get(dev, NULL); + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0) { + ret = dcp_firmware_metadata_v27(dcp); + if (ret) + goto err_piodma; + } + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0 && !dcp->num_firmware_clocks) + dcp->clk = devm_clk_get_optional(dev, NULL); + else + dcp->clk = devm_clk_get(dev, NULL); if (IS_ERR(dcp->clk)) { ret = dev_err_probe(dev, PTR_ERR(dcp->clk), "Unable to find clock\n"); @@ -3805,7 +3931,7 @@ static void dcp_platform_shutdown(struct platform_device *pdev) struct apple_dcp *dcp = platform_get_drvdata(pdev); if (dcp && dcp->hw.adopt_live_session && - dcp->fw_compat == DCP_FIRMWARE_H17P && + dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) { /* * This firmware keeps scanning across soft DPMS and has no @@ -3833,7 +3959,7 @@ static void dcp_drain_for_sleep(struct apple_dcp *dcp) { int pass; - if (!dcp->hw.adopt_live_session || dcp->fw_compat != DCP_FIRMWARE_H17P || + if (!dcp->hw.adopt_live_session || dcp->fw_compat < DCP_FIRMWARE_H17P || dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G) return; @@ -3957,7 +4083,23 @@ static const struct apple_dcp_hw_data apple_dcp_hw_dcpext = { .num_dptx_ports = 2, }; +static const struct apple_dcp_hw_data apple_dcp_hw_t6050 = { + .firmware_dma_bits = 41, + .num_dptx_ports = 0, + .adopt_live_session = true, + .firmware_compat = DCP_FIRMWARE_V_27_0, +}; + +static const struct apple_dcp_hw_data apple_dcp_hw_t6050_dcpext = { + .firmware_dma_bits = 41, + .num_dptx_ports = 2, + .adopt_live_session = true, + .firmware_compat = DCP_FIRMWARE_V_27_0, +}; + static const struct of_device_id of_match[] = { + { .compatible = "apple,t6050-dcp", .data = &apple_dcp_hw_t6050 }, + { .compatible = "apple,t6050-dcpext", .data = &apple_dcp_hw_t6050_dcpext }, { .compatible = "apple,t6020-dcp", .data = &apple_dcp_hw_t6020, }, { .compatible = "apple,t8112-dcp", .data = &apple_dcp_hw_t8112, }, { .compatible = "apple,t8140-dcp", .data = &apple_dcp_hw_t8140, }, diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index 3528f7a9dbc36..422571898345f 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -84,7 +84,7 @@ static u32 brightness_part2[] = { bool dcp_backlight_active(struct apple_dcp *dcp) { - return dcp->fw_compat == DCP_FIRMWARE_H17P && + return dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G && READ_ONCE(dcp->backlight.state.ready); } @@ -133,7 +133,7 @@ int dcp_backlight_configure(struct apple_dcp *dcp, u32 maximum, unsigned long flags; int ret; - if (dcp->fw_compat != DCP_FIRMWARE_H17P || !dcp_has_panel(dcp) || + if (dcp->fw_compat < DCP_FIRMWARE_H17P || !dcp_has_panel(dcp) || !dcp->crtc || !kick) return -EINVAL; @@ -364,7 +364,7 @@ int dcp_backlight_update(struct apple_dcp *dcp) dcp_backlight_kick(dcp); return 0; } - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) return -ENODATA; @@ -446,7 +446,7 @@ int dcp_backlight_register(struct apple_dcp *dcp) .scale = BACKLIGHT_SCALE_LINEAR, }; props.max_brightness = min(dcp->brightness.maximum, MAX_BRIGHTNESS_PART2 - 1); - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G && !dcp_backlight_active(dcp)) return -ENODATA; diff --git a/drivers/gpu/drm/apple/dptx-drive-v27.inc b/drivers/gpu/drm/apple/dptx-drive-v27.inc new file mode 100644 index 0000000000000..9b8c3e6af1fa2 --- /dev/null +++ b/drivers/gpu/drm/apple/dptx-drive-v27.inc @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Aurora Silicon */ +struct dptxport_drive_v27 { + __le32 status_or_count; + u8 reserved0[12]; + __le32 count_or_status; + u8 reserved1[12]; + struct { + __le32 type; + __le32 voltage; + __le32 pre; + } lane[]; +} __packed; + +static int dptxport_call_drive_v27(struct apple_epic_service *service, bool set, + const void *data, size_t data_size, + void *reply_, size_t reply_size) +{ + const struct dptxport_drive_v27 *request = data; + struct dptxport_drive_v27 *reply = reply_; + struct dptx_port *dptx = service->cookie; + union phy_configure_opts opts = {}; + u32 count; + size_t size; + int ret; + + if (data_size < sizeof(*request) || reply_size < sizeof(*reply)) + return -EINVAL; + count = le32_to_cpu(set ? request->count_or_status : request->status_or_count); + if (count > 4 || count > dptx->lane_count) + return -EINVAL; + size = sizeof(*request) + count * sizeof(request->lane[0]); + if ((set && data_size < size) || (!set && reply_size < size)) + return -EINVAL; + + if (set) { + for (u32 i = 0; i < count; i++) { + + if (le32_to_cpu(request->lane[i].type) || + le32_to_cpu(request->lane[i].voltage) > 3 || + le32_to_cpu(request->lane[i].pre) > 3 || + le32_to_cpu(request->lane[i].voltage) + + le32_to_cpu(request->lane[i].pre) > 3) + return -EINVAL; + opts.dp.voltage[i] = le32_to_cpu(request->lane[i].voltage); + opts.dp.pre[i] = le32_to_cpu(request->lane[i].pre); + } + opts.dp.lanes = count; + opts.dp.link_rate = dptx->phy_ops.dp.link_rate; + opts.dp.set_voltages = 1; + if (count && !service->ep->dcp->dptx_tunnel) { + if (!dptx->atcphy) + return -ENODEV; + ret = phy_configure(dptx->atcphy, &opts); + if (ret) + return ret; + } + for (u32 i = 0; i < count; i++) { + dptx->drive_voltage[i] = opts.dp.voltage[i]; + dptx->drive_pre[i] = opts.dp.pre[i]; + } + } + + memset(reply, 0, reply_size); + if (set) { + reply->count_or_status = cpu_to_le32(count); + } else { + + reply->status_or_count = cpu_to_le32(count); + } + + if (reply_size >= size) { + for (u32 i = 0; i < count; i++) { + reply->lane[i].voltage = cpu_to_le32(dptx->drive_voltage[i]); + reply->lane[i].pre = cpu_to_le32(dptx->drive_pre[i]); + } + } + return 0; +} diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 11091084f05e2..04dec7711575b 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -78,6 +78,8 @@ struct dptxport_apcall_set_tiled { __le32 retcode; }; +#include "dptx-drive-v27.inc" + /* * Ported from aurora-silicon/linux#8: a Thunderbolt DP tunnel uses the same * plain CORE|ATC|DIE|CONNECTED target as a direct alt-mode PHY. CORE is the @@ -266,6 +268,11 @@ dptxport_call_get_max_drive_settings(struct apple_epic_service *service, reply->retcode = cpu_to_le32(0); reply->max_drive_settings[0] = cpu_to_le32(0x3); reply->max_drive_settings[1] = cpu_to_le32(0x3); + if (service->ep->dcp->fw_compat == DCP_FIRMWARE_V_27_0) { + /* Protocol-27 drive settings: type, voltage, pre-emphasis. */ + reply->max_drive_settings[0] = cpu_to_le32(0); + memcpy(reply->_unk1, &reply->max_drive_settings[1], sizeof(__le32)); + } return 0; } @@ -650,11 +657,15 @@ dptxport_call_activate(struct apple_epic_service *service, * change for a tunnel (dptx->atcphy must stay in USB4/TBT mode the * whole connection, see dcp_tunnel_set_rate()). The DP IN adapter is * only woken (DPTX_INACTIVE=0) here, via dcp_tunnel_dpin_activate(); - * waking it earlier hangs the machine. Activate always replies - * success to DCP. + * waking it earlier hangs the machine. Report PHY activation failures + * to DCP instead of acknowledging a transmitter that is not ready. */ - if (dptx->atcphy && !dcp->phy_managed_by_typec) - phy_set_mode_ext(dptx->atcphy, PHY_MODE_DP, dcp->index); + if (dptx->atcphy && !dcp->phy_managed_by_typec) { + int ret = phy_set_mode_ext(dptx->atcphy, PHY_MODE_DP, dcp->index); + + if (ret) + return ret; + } if (dcp->dptx_tunnel) dcp_tunnel_dpin_activate(dcp, true); @@ -672,12 +683,19 @@ dptxport_call_deactivate(struct apple_epic_service *service, { struct dptx_port *dptx = service->cookie; struct apple_dcp *dcp = service->ep->dcp; + int ret; dev_info(dcp->dev, "DPTXPort: DEACTIVATE\n"); + ret = dcp_direct_crossbar_link(dcp, false); + if (ret) + return ret; if (dcp->dptx_tunnel) dcp_tunnel_dpin_activate(dcp, false); - if (dptx->atcphy && !dcp->phy_managed_by_typec) - phy_set_mode_ext(dptx->atcphy, PHY_MODE_INVALID, 0); + if (dptx->atcphy && !dcp->phy_managed_by_typec) { + ret = phy_set_mode_ext(dptx->atcphy, PHY_MODE_INVALID, 0); + if (ret) + return ret; + } memcpy(reply, data, min(reply_size, data_size)); if (reply_size >= 4) @@ -700,7 +718,11 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, min(data_size, (size_t)64), true); switch (idx) { - case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG: + case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG: { + int ret = dcp_direct_crossbar_link(service->ep->dcp, false); + + if (ret) + return ret; /* * Ported from aurora-silicon/linux#8: a re-link on an * established tunnel takes the crossbar connection down @@ -709,11 +731,14 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, if (service->ep->dcp->dptx_tunnel && dptx->link_rate) dcp_tunnel_crossbar_down(service->ep->dcp); return dptxport_call_will_change_link_config(service); + } case DPTX_APCALL_DID_CHANGE_LINK_CONFIG: { int ret = dptxport_call_did_change_link_config(service); if (!ret && service->ep->dcp->dptx_tunnel && dptx->link_rate) dcp_tunnel_crossbar_up(service->ep->dcp); + if (!ret && dptx->link_rate) + ret = dcp_direct_crossbar_link(service->ep->dcp, true); return ret; } case DPTX_APCALL_GET_MAX_LINK_RATE: @@ -760,9 +785,15 @@ static int dptxport_call(struct apple_epic_service *service, u32 idx, memcpy(reply, data, min(reply_size, data_size)); return dptxport_call_set_tiled_display_hint(reply, reply_size); case DPTX_APCALL_GET_DRIVE_SETTINGS: + if (service->ep->dcp->fw_compat == DCP_FIRMWARE_V_27_0) + return dptxport_call_drive_v27(service, false, data, data_size, + reply, reply_size); return dptxport_call_get_drive_settings(service, data, data_size, reply, reply_size); case DPTX_APCALL_SET_DRIVE_SETTINGS: + if (service->ep->dcp->fw_compat == DCP_FIRMWARE_V_27_0) + return dptxport_call_drive_v27(service, true, data, data_size, + reply, reply_size); return dptxport_call_set_drive_settings(service, data, data_size, reply, reply_size); case DPTX_APCALL_ACTIVATE: @@ -856,7 +887,8 @@ int dptxep_init(struct apple_dcp *dcp) init_completion(&dcp->dptxport[0].linkcfg_completion); init_completion(&dcp->dptxport[1].linkcfg_completion); - dcp->dptxep = afk_init(dcp, DPTX_ENDPOINT, dptxep_ops); + dcp->dptxep = afk_init(dcp, dcp->fw_compat == DCP_FIRMWARE_V_27_0 ? + DPTX_ENDPOINT_V27 : DPTX_ENDPOINT, dptxep_ops); if (IS_ERR(dcp->dptxep)) return PTR_ERR(dcp->dptxep); diff --git a/drivers/gpu/drm/apple/dptxep.h b/drivers/gpu/drm/apple/dptxep.h index 885e3675f9db5..f7dea18daba85 100644 --- a/drivers/gpu/drm/apple/dptxep.h +++ b/drivers/gpu/drm/apple/dptxep.h @@ -59,6 +59,7 @@ struct dptx_port { u32 lane_count; u32 link_rate, pending_link_rate; u32 drive_settings[2]; + u32 drive_voltage[4], drive_pre[4]; /* * Per-boot invocation counters for the validate/connect/request/ * release AFK calls, used together with the caller return address diff --git a/drivers/gpu/drm/apple/iomfb-boolean-properties-v27.inc b/drivers/gpu/drm/apple/iomfb-boolean-properties-v27.inc new file mode 100644 index 0000000000000..b7b617b37994d --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb-boolean-properties-v27.inc @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Aurora Silicon */ + +struct dcp_boolean_property_v27 { + struct list_head node; + char name[64]; + bool value; +}; + +static bool trampoline_set_boolean_property_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const u8 *request = in; + struct dcp_boolean_property_v27 *property; + char name[64]; + + trace_iomfb_callback(dcp, tag, __func__); + + memcpy(name, request, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + put_unaligned_le32(0, out); + + list_for_each_entry(property, &dcp->iomfb_boolean_properties, node) + if (!strcmp(property->name, name)) + goto update; + if (dcp->iomfb_boolean_property_count >= 64) + return true; + property = kzalloc(sizeof(*property), GFP_KERNEL); + if (!property) + return true; + memcpy(property->name, name, sizeof(name)); + dcp->iomfb_boolean_property_count++; + list_add_tail(&property->node, &dcp->iomfb_boolean_properties); +update: + property->value = request[64] & 1; + put_unaligned_le32(1, out); + return true; +} + +void iomfb_firmware_state_cleanup_v27(void *data) +{ + struct apple_dcp *dcp = data; + struct dcp_boolean_property_v27 *property, *next; + + external_counts_cleanup_v27(dcp); + list_for_each_entry_safe(property, next, &dcp->iomfb_boolean_properties, node) { + list_del(&property->node); + kfree(property); + dcp->iomfb_boolean_property_count--; + } +} diff --git a/drivers/gpu/drm/apple/iomfb-dfb.h b/drivers/gpu/drm/apple/iomfb-dfb.h new file mode 100644 index 0000000000000..30e84ab8366c3 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb-dfb.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Aurora Silicon */ + +#ifndef __APPLE_IOMFB_DFB_H__ +#define __APPLE_IOMFB_DFB_H__ + +#include +#include +#include +#include +#include + +static inline u64 apple_dcp_dfb_round_size(u64 size) +{ + unsigned int bits = fls64(size); + u64 unit, half, tail; + + if (bits <= 24) + return size; + unit = 1ULL << (bits - 24); + half = unit >> 1; + tail = size & (unit - 1); + size &= ~(unit - 1); + if (tail > half || (tail == half && (size & unit))) + size += unit; + return size; +} + +static inline u64 apple_dcp_dfb_plane_size(u32 width, u32 height, u32 bpe, + u32 compression) +{ + u64 stride, data, cols, rows, tw = 16, th = 16; + unsigned int order; + + if (!compression) { + stride = ALIGN((u64)width * bpe, 128); + return ALIGN(apple_dcp_dfb_round_size(stride * height), 0x4000); + } + if (compression == 2 && bpe > 4) { + th = 8; + if (bpe == 16) + tw = 8; + } + cols = DIV_ROUND_UP(width, tw); + rows = DIV_ROUND_UP(height, th); + if (compression == 3) { + stride = tw * th * cols * bpe; + data = ALIGN(rows * stride, 128); + return data + 8 * roundup_pow_of_two(cols) * roundup_pow_of_two(rows); + } + order = ilog2(0x4000 / bpe); + stride = ALIGN((u64)width * bpe, (u64)bpe << (order / 2)); + data = ALIGN(ALIGN((u64)height, 1ULL << ((order + 1) / 2)) * stride, 128); + return data + ALIGN(2 * roundup_pow_of_two(cols) * roundup_pow_of_two(rows), 128); +} + +static inline int apple_dcp_dfb_allocation(u32 width, u32 height, u32 format, + u32 compression, u64 region_size, + u32 *surface_format, u32 *allocation) +{ + u32 bpe; + u64 size; + bool alpha_plane = false; + + if (!width || !height || width > 16384 || height > 16384 || !region_size) + return -EINVAL; + if (compression != 0 && compression != 2 && compression != 3) + return -EOPNOTSUPP; + switch (format) { + case 0x62336138: + if (compression == 2) { + format = 0x77343061; + bpe = 8; + } else { + bpe = 4; + alpha_plane = true; + } + break; + case 0x77343061: + bpe = 8; + break; + case 0x42475241: + case 0x77333072: + case 0x6c313072: + bpe = 4; + break; + default: + return -EOPNOTSUPP; + } + size = apple_dcp_dfb_plane_size(width, height, bpe, compression); + if (alpha_plane) + size += apple_dcp_dfb_plane_size(width, height, 1, compression); + if (size > U32_MAX) + return -EOVERFLOW; + if (compression) + size = ALIGN(apple_dcp_dfb_round_size(size), 0x4000); + if (size > region_size) + return -ENOSPC; + if (!size || size > U32_MAX) + return -EOVERFLOW; + *surface_format = format; + *allocation = size; + return 0; +} +#endif diff --git a/drivers/gpu/drm/apple/iomfb-display-identity-v27.inc b/drivers/gpu/drm/apple/iomfb-display-identity-v27.inc new file mode 100644 index 0000000000000..1b3710c1c7627 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb-display-identity-v27.inc @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Aurora Silicon */ + +struct dcp_display_product_request_v27 { + u8 vendor[10]; + __le32 vendor_id; + __le32 vendor_length; + __le32 product_id; + u8 vendor_id_null, product_id_null; +} __packed; + +struct dcp_display_product_reply_v27 { + u8 vendor[10]; + __le32 vendor_id; + __le32 product_id; + u8 success, padding; +} __packed; + +static_assert(sizeof(struct dcp_display_product_request_v27) == 24); +static_assert(sizeof(struct dcp_display_product_reply_v27) == 20); +static_assert(offsetof(struct dcp_display_product_reply_v27, product_id) == 14); + +struct dcp_display_identity_callback_v27 { + void *output; + enum dcp_context_id context; +}; + +static void display_identity_done_v27(struct apple_dcp *dcp, void *out, void *cookie) +{ + const struct dcp_display_product_reply_v27 *reply = out; + struct dcp_display_identity_callback_v27 *cb = cookie; + bool vendor_matches = reply && (!reply->vendor[0] || + (!memcmp(reply->vendor, "APP", 3) && !reply->vendor[3])); + bool display_x1 = reply && (reply->success & 1) && vendor_matches && + (le32_to_cpu(reply->product_id) >> 1) == 0x5721; + + put_unaligned_le32(display_x1, cb->output); + dcp_ack(dcp, cb->context); + kfree(cb); +} + +static bool trampoline_display_identity_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + static const struct dcp_method_entry method = { + .name = "display_product_id", .tag = {'A', '3', '7', '5'}, + }; + static const enum dcp_context_id contexts[] = { + DCP_CONTEXT_CB, DCP_CONTEXT_ASYNC, DCP_CONTEXT_OOBCB, DCP_CONTEXT_OOBASYNC, + }; + struct dcp_channel *channels[] = { + &dcp->ch_cb, &dcp->ch_async, &dcp->ch_oobcb, &dcp->ch_oobasync, + }; + struct dcp_display_product_request_v27 request = { + .vendor_length = cpu_to_le32(10), + }; + struct dcp_display_identity_callback_v27 *cb; + int context = -1; + + trace_iomfb_callback(dcp, tag, __func__); + + for (unsigned int i = 0; i < ARRAY_SIZE(channels); i++) { + struct dcp_channel *ch = channels[i]; + + if (ch->depth && ch->output[ch->depth - 1] == out) { + context = contexts[i]; + break; + } + } + if (WARN_ON(context < 0)) + return true; + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + if (!cb) + return true; + cb->output = out; + cb->context = context; + if (dcp_push_nested(dcp, context, &method, sizeof(request), + sizeof(struct dcp_display_product_reply_v27), &request, + display_identity_done_v27, cb)) { + kfree(cb); + return true; + } + return false; +} diff --git a/drivers/gpu/drm/apple/iomfb-external-counts-v27.inc b/drivers/gpu/drm/apple/iomfb-external-counts-v27.inc new file mode 100644 index 0000000000000..ab5602821d3ca --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb-external-counts-v27.inc @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Aurora Silicon */ + +static DEFINE_SPINLOCK(external_counts_lock_v27); +static u32 external_pipe_count_v27; +static u32 external_apple_display_count_v27; +static u32 external_suppression_count_v27; + +static bool trampoline_update_external_counts_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const u8 *request = in; + bool plug = request[0] & 1; + u32 apple_display = request[1] & 1; + u32 pipes = get_unaligned_le32(request + 2); + unsigned long flags; + + trace_iomfb_callback(dcp, tag, __func__); + + if (dcp->connector_type == DRM_MODE_CONNECTOR_eDP) + return true; + spin_lock_irqsave(&external_counts_lock_v27, flags); + if ((plug && (pipes > U32_MAX - external_pipe_count_v27 || + apple_display > U32_MAX - external_apple_display_count_v27)) || + (!plug && (pipes > dcp->external_pipes_v27 || + apple_display > dcp->external_displays_v27))) { + spin_unlock_irqrestore(&external_counts_lock_v27, flags); + dev_warn_ratelimited(dcp->dev, "invalid external display count update\n"); + return true; + } + dcp->external_pipes_v27 += plug ? pipes : -pipes; + dcp->external_displays_v27 += plug ? apple_display : -apple_display; + external_pipe_count_v27 += plug ? pipes : -pipes; + external_apple_display_count_v27 += plug ? apple_display : -apple_display; + spin_unlock_irqrestore(&external_counts_lock_v27, flags); + return true; +} + +static bool trampoline_get_external_counts_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const u8 *request = in; + unsigned long flags; + u32 counts[3]; + + trace_iomfb_callback(dcp, tag, __func__); + spin_lock_irqsave(&external_counts_lock_v27, flags); + counts[0] = external_pipe_count_v27; + counts[1] = external_apple_display_count_v27; + counts[2] = external_suppression_count_v27; + spin_unlock_irqrestore(&external_counts_lock_v27, flags); + + for (unsigned int i = 0; i < ARRAY_SIZE(counts); i++) + if (!(request[12 + i] & 1)) + put_unaligned_le32(counts[i], (u8 *)out + 4 * i); + return true; +} + +static bool trampoline_apply_suppression_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const u8 *request = in; + unsigned long flags; + + trace_iomfb_callback(dcp, tag, __func__); + spin_lock_irqsave(&external_counts_lock_v27, flags); + if (((request[0] & 1) && external_suppression_count_v27 == U32_MAX) || + (!(request[0] & 1) && !dcp->external_suppression_v27)) { + spin_unlock_irqrestore(&external_counts_lock_v27, flags); + return true; + } + dcp->external_suppression_v27 += (request[0] & 1) ? 1U : -1U; + external_suppression_count_v27 += (request[0] & 1) ? 1U : -1U; + spin_unlock_irqrestore(&external_counts_lock_v27, flags); + return true; +} + +static void external_counts_cleanup_v27(struct apple_dcp *dcp) +{ + unsigned long flags; + + spin_lock_irqsave(&external_counts_lock_v27, flags); + external_pipe_count_v27 -= dcp->external_pipes_v27; + external_apple_display_count_v27 -= dcp->external_displays_v27; + external_suppression_count_v27 -= dcp->external_suppression_v27; + dcp->external_pipes_v27 = 0; + dcp->external_displays_v27 = 0; + dcp->external_suppression_v27 = 0; + spin_unlock_irqrestore(&external_counts_lock_v27, flags); +} diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 8c8cfbb5cc579..1f833dfc2b086 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -46,6 +46,10 @@ static int dcp_tx_offset(enum dcp_context_id id) case DCP_CONTEXT_OOBCB: case DCP_CONTEXT_OOBCMD: return 0x08000; + case DCP_CONTEXT_ASYNC_CB: + return 0x20000; + case DCP_CONTEXT_OOBASYNC_CB: + return 0x28000; default: return -EINVAL; } @@ -54,8 +58,10 @@ static int dcp_tx_offset(enum dcp_context_id id) static int dcp_channel_offset(enum dcp_context_id id) { switch (id) { + case DCP_CONTEXT_ASYNC_CB: case DCP_CONTEXT_ASYNC: return 0x40000; + case DCP_CONTEXT_OOBASYNC_CB: case DCP_CONTEXT_OOBASYNC: return 0x48000; case DCP_CONTEXT_CB: @@ -125,8 +131,10 @@ static struct dcp_channel *dcp_get_channel(struct apple_dcp *dcp, return &dcp->ch_oobcb; case DCP_CONTEXT_OOBCMD: return &dcp->ch_oobcmd; + case DCP_CONTEXT_ASYNC_CB: case DCP_CONTEXT_ASYNC: return &dcp->ch_async; + case DCP_CONTEXT_OOBASYNC_CB: case DCP_CONTEXT_OOBASYNC: return &dcp->ch_oobasync; default: @@ -162,7 +170,7 @@ static u8 dcp_pop_depth(u8 *depth) /* Older firmware and the H17G method profile retain their existing transport. */ static bool iomfb_uses_queue(struct apple_dcp *dcp) { - return dcp->fw_compat == DCP_FIRMWARE_H17P && + return dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G; } @@ -193,7 +201,8 @@ static int iomfb_enqueue_opaque_x(struct apple_dcp *dcp) struct iomfb_opaque_x_transaction *opaque; lockdep_assert_held(&dcp->iomfb.lock); - if (atomic_read(&dcp->iomfb.opaque_x_state) != IOMFB_OPAQUE_X_NEEDED) + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0 || + atomic_read(&dcp->iomfb.opaque_x_state) != IOMFB_OPAQUE_X_NEEDED) return 0; if (dcp->iomfb.queued >= 32) return -EBUSY; @@ -560,21 +569,14 @@ static int iomfb_queue_command(struct apple_dcp *dcp, bool oob, } /* Call a DCP function given by a tag */ -void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *call, - u32 in_len, u32 out_len, void *data, dcp_callback_t cb, - void *cookie) +static void dcp_push_context(struct apple_dcp *dcp, enum dcp_context_id context, + const struct dcp_method_entry *call, u32 in_len, + u32 out_len, void *data, dcp_callback_t cb, void *cookie) { struct dcp_method_entry resolved = *call; - /* Reply chains stay on the serialized receiver; callers enqueue copies. */ - if (iomfb_uses_queue(dcp) && READ_ONCE(dcp->iomfb.owner) != current) { - if (iomfb_queue_command(dcp, oob, call, in_len, out_len, - data, cb, cookie)) - WRITE_ONCE(dcp->crashed, true); - return; - } - if (dcp->fw_compat == DCP_FIRMWARE_H17P && READ_ONCE(dcp->crashed)) + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && READ_ONCE(dcp->crashed)) return; if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G && @@ -582,7 +584,6 @@ void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *ca memcpy(resolved.tag, call->tag_h17g, sizeof(resolved.tag)); call = &resolved; - enum dcp_context_id context = dcp_call_context(dcp, oob); struct dcp_channel *ch = dcp_get_channel(dcp, context); struct dcp_packet_header header = { @@ -636,6 +637,54 @@ void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *ca dcpep_msg(context, data_len, offset)); } +void dcp_push(struct apple_dcp *dcp, bool oob, + const struct dcp_method_entry *call, u32 in_len, u32 out_len, + void *data, dcp_callback_t cb, void *cookie) +{ + /* Reply chains stay on the serialized receiver; callers enqueue copies. */ + if (iomfb_uses_queue(dcp) && READ_ONCE(dcp->iomfb.owner) != current) { + if (iomfb_queue_command(dcp, oob, call, in_len, out_len, + data, cb, cookie)) + WRITE_ONCE(dcp->crashed, true); + return; + } + + dcp_push_context(dcp, dcp_call_context(dcp, oob), call, + in_len, out_len, data, cb, cookie); +} + +/* A nested query must return on the firmware-initiated stream it answers. */ +int dcp_push_nested(struct apple_dcp *dcp, enum dcp_context_id origin, + const struct dcp_method_entry *call, u32 in_len, u32 out_len, + void *data, dcp_callback_t cb, void *cookie) +{ + enum dcp_context_id context; + struct dcp_channel *ch; + + if (READ_ONCE(dcp->iomfb.owner) != current || READ_ONCE(dcp->crashed)) + return -ESHUTDOWN; + switch (origin) { + case DCP_CONTEXT_CB: + case DCP_CONTEXT_OOBCB: + context = origin; + break; + case DCP_CONTEXT_ASYNC: + context = DCP_CONTEXT_ASYNC_CB; + break; + case DCP_CONTEXT_OOBASYNC: + context = DCP_CONTEXT_OOBASYNC_CB; + break; + default: + return -EINVAL; + } + ch = dcp_get_channel(dcp, context); + if (!ch || !ch->depth || ch->depth >= DCP_MAX_CALL_DEPTH || + (u64)sizeof(struct dcp_packet_header) + in_len + out_len > 0x8000) + return -EINVAL; + dcp_push_context(dcp, context, call, in_len, out_len, data, cb, cookie); + return READ_ONCE(dcp->crashed) ? -EPROTO : 0; +} + /* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */ int dcp_parse_tag(char tag[4]) { @@ -781,7 +830,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, u8 depth; bool handled; - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && (length < sizeof(*hdr) || !ch || ch->depth >= DCP_MAX_CALL_DEPTH || offset >= 0x8000 || length > 0x8000 - offset)) { dev_err(dev, "invalid IOMFB callback envelope\n"); @@ -792,12 +841,14 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, tag = dcp_parse_tag(hdr->tag); handled = tag >= 0 && tag < IOMFB_MAX_CB && dcp->cb_handlers && dcp->cb_handlers[tag]; - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && (tag < 0 || tag >= IOMFB_MAX_CB || (u64)sizeof(*hdr) + hdr->in_len + hdr->out_len != length || (dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G && handled && - !iomfb_check_callback_h17p(dcp, tag, hdr->in_len, hdr->out_len)))) { + !(dcp->fw_compat == DCP_FIRMWARE_V_27_0 ? + iomfb_v27_callback_size_valid(tag, hdr->in_len, hdr->out_len) : + iomfb_check_callback_h17p(dcp, tag, hdr->in_len, hdr->out_len))))) { dev_err(dev, "unqualified IOMFB callback %d (%u, %u)\n", tag, hdr->in_len, hdr->out_len); dcp->crashed = true; @@ -817,7 +868,7 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, * waits for the ack forever and the outer call never returns. * On H17P answer it with a zeroed output instead. */ - if (dcp->fw_compat == DCP_FIRMWARE_H17P) { + if (dcp->fw_compat >= DCP_FIRMWARE_H17P) { if (hdr->out_len) memset(out, 0, hdr->out_len); depth = dcp_push_depth(&ch->depth); @@ -883,7 +934,7 @@ static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context) * its output cannot be trusted. The tag is left out of the check: it * has only been seen preserved on 13.5 firmware. */ - if (dcp->fw_compat != DCP_FIRMWARE_H17P) { + if (dcp->fw_compat < DCP_FIRMWARE_H17P) { record = out - sent->in_len - sizeof(*record); if (record->in_len != sent->in_len || record->out_len != sent->out_len) { @@ -1096,6 +1147,9 @@ int dcp_crtc_atomic_modeset(struct drm_crtc *crtc, case DCP_FIRMWARE_H17P: ret = iomfb_modeset_h17p(dcp, crtc_state); break; + case DCP_FIRMWARE_V_27_0: + ret = iomfb_modeset_v27(dcp, crtc_state); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); @@ -1286,8 +1340,9 @@ bool iomfb_present_complete_h17p(struct apple_dcp *dcp) if (transaction->brightness_only) return false; } - atomic_cmpxchg(&dcp->iomfb.opaque_x_state, IOMFB_OPAQUE_X_WAITING, - IOMFB_OPAQUE_X_NEEDED); + if (dcp->fw_compat != DCP_FIRMWARE_V_27_0) + atomic_cmpxchg(&dcp->iomfb.opaque_x_state, IOMFB_OPAQUE_X_WAITING, + IOMFB_OPAQUE_X_NEEDED); iomfb_scanout_complete_h17p(dcp); return true; } @@ -1308,7 +1363,10 @@ static void iomfb_backlight_start(struct apple_dcp *dcp, return; dcp->swap.h17p = scanout->request; - iomfb_present_backlight_h17p(dcp); + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0) + iomfb_present_backlight_v27(dcp); + else + iomfb_present_backlight_h17p(dcp); } static void iomfb_backlight_release(struct iomfb_transaction *transaction) @@ -1365,7 +1423,10 @@ static void iomfb_atomic_start(struct apple_dcp *dcp, return; } dcp->iomfb.next_scanout = atomic->scanout; - iomfb_flush_h17p(dcp, atomic->crtc, atomic->state); + if (dcp->fw_compat == DCP_FIRMWARE_V_27_0) + iomfb_flush_v27(dcp, atomic->crtc, atomic->state); + else + iomfb_flush_h17p(dcp, atomic->crtc, atomic->state); } static void iomfb_atomic_release(struct iomfb_transaction *transaction) @@ -1453,6 +1514,9 @@ void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) case DCP_FIRMWARE_H17P: iomfb_flush_h17p(dcp, crtc, state); break; + case DCP_FIRMWARE_V_27_0: + iomfb_flush_v27(dcp, crtc, state); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); break; @@ -1471,6 +1535,9 @@ static void iomfb_start(struct apple_dcp *dcp) case DCP_FIRMWARE_H17P: iomfb_start_h17p(dcp); break; + case DCP_FIRMWARE_V_27_0: + iomfb_start_v27(dcp); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); break; @@ -1546,7 +1613,7 @@ int iomfb_start_rtkit(struct apple_dcp *dcp) * has nothing answering it on the AP side, and without this one the * display DART faults. */ - if (dcp->fw_compat == DCP_FIRMWARE_H17P) { + if (dcp->fw_compat >= DCP_FIRMWARE_H17P) { ret = apple_rtkit_start_ep(dcp->rtk, REMOTE_ALLOC_ENDPOINT); if (ret) return ret; @@ -1581,6 +1648,9 @@ void iomfb_shutdown(struct apple_dcp *dcp) case DCP_FIRMWARE_H17P: iomfb_shutdown_h17p(dcp); break; + case DCP_FIRMWARE_V_27_0: + iomfb_shutdown_v27(dcp); + break; default: WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat); break; diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index efed16220d947..2eb251d956105 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -15,6 +15,7 @@ enum dcp_context_id { /* Callback */ DCP_CONTEXT_CB = 0, + DCP_CONTEXT_ASYNC_CB = 1, /* Command */ DCP_CONTEXT_CMD = 2, @@ -24,6 +25,7 @@ enum dcp_context_id { /* Out-of-band callback */ DCP_CONTEXT_OOBCB = 4, + DCP_CONTEXT_OOBASYNC_CB = 5, /* Out-of-band command */ DCP_CONTEXT_OOBCMD = 6, @@ -183,6 +185,8 @@ enum dcpep_method { * H17P pipe-configuration calls made before the first swap. Most * names are unknown; they are identified by method number. */ + dcpep_pipe_query_353, + dcpep_pipe_set_352, dcpep_pipe_cfg_415, dcpep_pipe_cfg_031, dcpep_pipe_cfg_414, diff --git a/drivers/gpu/drm/apple/iomfb_internal.h b/drivers/gpu/drm/apple/iomfb_internal.h index 75e9d7b0e8cc8..597703a9b0e8d 100644 --- a/drivers/gpu/drm/apple/iomfb_internal.h +++ b/drivers/gpu/drm/apple/iomfb_internal.h @@ -112,3 +112,7 @@ void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context); /* The user may own drm_display_mode, so we need to search for our copy */ struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp, const struct drm_display_mode *mode); + +int dcp_push_nested(struct apple_dcp *dcp, enum dcp_context_id origin, + const struct dcp_method_entry *call, u32 in_len, u32 out_len, + void *data, dcp_callback_t cb, void *cookie); diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index f14031085142f..1177ba6d01e3b 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -29,6 +29,7 @@ #include "dcp-internal.h" #include "iomfb.h" #include "iomfb_internal.h" +#include "iomfb-dfb.h" #include "parser.h" #include "trace.h" #include "version_utils.h" @@ -76,10 +77,24 @@ static void dcp_swap_submit(struct apple_dcp *dcp, bool oob, cb(dcp, NULL, cookie); return; } +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + iomfb_serialize_present_v27(&dcp->present_v27, request); + dcp->present_v27.swap.brightness_update = + dcp->present_h17p.swap[0x354] & 1; + dcp->present_v27.swap.brightness_nits = + get_unaligned_le64(dcp->present_h17p.swap + 0x35e); + data = &dcp->present_v27; +#else data = &dcp->present_h17p; +#endif } - dcp_push(dcp, oob, &dcp_methods[dcpep_swap_submit], sizeof(*request), + dcp_push(dcp, oob, &dcp_methods[dcpep_swap_submit], +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + sizeof(struct dcp_swap_submit_req_v27), +#else + sizeof(*request), +#endif sizeof(struct dcp_swap_submit_resp_h17p), (void *)data, cb, cookie); } #else @@ -114,7 +129,7 @@ DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, * programmed, which is the panel's native mode, instead of requesting one. */ #if DCP_FW_VER >= DCP_FW_VERSION(26, 0, 0) -#define DCP_INHERIT_BOOT_MODE true +#define DCP_INHERIT_BOOT_MODE (DCP_FW_VER < DCP_FW_VERSION(27, 0, 0)) #else #define DCP_INHERIT_BOOT_MODE false #endif @@ -127,7 +142,7 @@ DCP_THUNK_INOUT(dcp_set_power_state, dcpep_set_power_state, * still sends its clear swap first. */ #if DCP_FW_VER >= DCP_FW_VERSION(26, 0, 0) -#define DCP_HAS_CLIENT_TEARDOWN false +#define DCP_HAS_CLIENT_TEARDOWN (DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0)) #else #define DCP_HAS_CLIENT_TEARDOWN true #endif @@ -330,7 +345,7 @@ static_assert(sizeof(struct dcp_h17p_dfb_surface_req) == 0x230); DCP_THUNK_INOUT(dcp_register_dfb_surface, dcpep_register_dfb_surface, struct dcp_h17p_dfb_surface_req, u32); -static void dcp_ack_dfb_surface(struct apple_dcp *dcp) +static void __maybe_unused dcp_ack_dfb_surface(struct apple_dcp *dcp) { struct dcp_channel *ch = &dcp->ch_cb; u8 *succ = ch->output[ch->depth - 1]; @@ -345,7 +360,7 @@ static void dcp_ack_dfb_surface(struct apple_dcp *dcp) * default surface ID. The bytes at +0x29..+0x2c are an opaque word whose * meaning is not known. */ -static void dcp_fill_dfb_surface(struct apple_dcp *dcp, u8 *s) +static void __maybe_unused dcp_fill_dfb_surface(struct apple_dcp *dcp, u8 *s) { s[0x002] = 0x01; memcpy(s + 0x00b, "8a3b", 4); @@ -373,7 +388,14 @@ static void dcp_fill_dfb_surface(struct apple_dcp *dcp, u8 *s) */ static void complete_dfb_surface(struct apple_dcp *dcp, void *out, void *cookie) { +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + struct dcp_channel *ch = &dcp->ch_cb; + + *(u8 *)ch->output[ch->depth - 1] = out && !get_unaligned_le32(out); + dcp_ack(dcp, DCP_CONTEXT_CB); +#else dcp_ack_dfb_surface(dcp); +#endif } static bool iomfbep_cb_create_dfb_surface(struct apple_dcp *dcp, int tag, @@ -393,12 +415,45 @@ static bool iomfbep_cb_create_dfb_surface(struct apple_dcp *dcp, int tag, if (!req) return true; + +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + { + struct dcp_dfb_dimensions_h17p dim; + struct dcp_surface *surface = (void *)req->surface; + u32 format, allocation; + int ret = -EINVAL; + + memcpy(&dim, in, sizeof(dim)); + if (dcp->dfb_format_valid && dcp->dfb_compression_valid) + ret = apple_dcp_dfb_allocation(dim.width, dim.height, + dcp->dfb_format, dcp->dfb_compression, + dcp->dfb_region_size, &format, &allocation); + if (ret) { + dev_err(dcp->dev, "unsupported DFB layout %ux%u format=%#x compression=%u (%d)\n", + dim.width, dim.height, dcp->dfb_format, + dcp->dfb_compression, ret); + *(u8 *)out = false; + kfree(req); + return true; + } + + surface->is_premultiplied = 1; + surface->format = format; + surface->pix_size = 1; + surface->pel_w = 1; + surface->pel_h = 1; + surface->buf_size = allocation; + surface->surface_id = 5; + surface->has_comp = 1; + surface->has_planes = 1; + } +#else dcp_fill_dfb_surface(dcp, req->surface); +#endif dcp_register_dfb_surface(dcp, false, req, complete_dfb_surface, NULL); kfree(req); - /* deferred ACK: the A472 completion reports success for us */ return false; } #endif @@ -455,7 +510,11 @@ static bool iomfbep_cb_match_backlight_service(struct apple_dcp *dcp, int tag, v static void iomfb_cb_pr_publish(struct apple_dcp *dcp, struct iomfb_property *prop) { switch (prop->id) { +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + case 20: +#else case IOMFB_PROPERTY_NITS: +#endif { #if DCP_FW_VERSION(26, 0, 0) <= DCP_FW_VER /* Measured H17P takeover comes from the powerlog hint interface. */ @@ -854,7 +913,12 @@ static struct DCP_FW_NAME(dcp_map_reg_resp) dcpep_cb_map_reg(struct apple_dcp *d * dma_map_resource() them instead fails and hands the firmware * DMA_MAPPING_ERROR as a device address. */ - dma_addr_t dva = rsrc->start; + dma_addr_t dva = +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + dcp->disp_firmware_base[req->index]; +#else + rsrc->start; +#endif #elif DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) dma_addr_t dva = dma_map_resource(dcp->dev, rsrc->start, resource_size(rsrc), DMA_BIDIRECTIONAL, 0); @@ -862,7 +926,12 @@ static struct DCP_FW_NAME(dcp_map_reg_resp) dcpep_cb_map_reg(struct apple_dcp *d #endif return (struct DCP_FW_NAME(dcp_map_reg_resp)){ - .addr = rsrc->start, + .addr = +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + dcp->disp_firmware_base[req->index], +#else + rsrc->start, +#endif .length = resource_size(rsrc), #if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) .dva = dva, @@ -1106,6 +1175,27 @@ static void boot_1_11(struct apple_dcp *dcp, void *out, void *cookie) static void boot_1_10(struct apple_dcp *dcp, void *out, void *cookie) { +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + struct dcp_dfb_dimensions_h17p dim; + + if (!out) + return; + memcpy(&dim, out, sizeof(dim)); + + if (dim.width > 16384 || dim.height > 16384 + ) { + struct dcp_channel *ch = &dcp->ch_cb; + + dev_err(dcp->dev, "invalid default framebuffer dimensions %ux%u\n", + dim.width, dim.height); + *(u8 *)ch->output[ch->depth - 1] = false; + dcp_ack(dcp, DCP_CONTEXT_CB); + return; + } + + dcp_set_dfb_dimensions(dcp, false, &dim, boot_1_11, NULL); + +#else struct dcp_dfb_dimensions_h17p dim; /* @@ -1126,12 +1216,25 @@ static void boot_1_10(struct apple_dcp *dcp, void *out, void *cookie) dim.width, dim.height); dcp_set_dfb_dimensions(dcp, false, &dim, boot_1_11, NULL); + +#endif } static void boot_1_9(struct apple_dcp *dcp, void *out, void *cookie) { u32 zero = 0; +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + struct dcp_dfb_dimensions_h17p dim; + + if (!out) + return; + memcpy(&dim, out, sizeof(dim)); + if (dim.width && dim.height) { + boot_2(dcp, out, cookie); + return; + } +#endif dcp_get_dfb_state(dcp, false, &zero, boot_1_10, NULL); } @@ -1139,6 +1242,12 @@ static void boot_1_8(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_dfb_layout_req_h17p req = {}; +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + if (!out) + return; + dcp->dfb_format_valid = *((u8 *)out + 0x28) & 1; + memcpy(&dcp->dfb_format, out, sizeof(dcp->dfb_format)); +#endif dcp_get_dfb_layout(dcp, false, &req, boot_1_9, NULL); } @@ -1146,6 +1255,12 @@ static void boot_1_7(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_dfb_info_h17p info = {}; +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + if (!out) + return; + dcp->dfb_compression_valid = *((u8 *)out + 4) & 1; + memcpy(&dcp->dfb_compression, out, sizeof(dcp->dfb_compression)); +#endif dcp_get_dfb_info(dcp, false, &info, boot_1_8, NULL); } @@ -1153,6 +1268,16 @@ static void boot_1_6(struct apple_dcp *dcp, void *out, void *cookie) { u64 zero = 0; +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + if (!out) + return; + if (!(*(u8 *)out & 1)) { + struct dcp_dfb_layout_req_h17p req = {}; + + dcp_get_dfb_layout(dcp, false, &req, boot_1_9, NULL); + return; + } +#endif dcp_get_dfb_compression_info(dcp, false, &zero, boot_1_7, NULL); } #endif @@ -1319,6 +1444,7 @@ static void release_swap_cookie(struct kref *ref) */ static void dcp_h17p_prepare_swap(struct apple_dcp *dcp) { +#if DCP_FW_VER < DCP_FW_VERSION(27, 0, 0) struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_UNION(dcp->swap); unsigned int i; @@ -1368,6 +1494,7 @@ static void dcp_h17p_prepare_swap(struct apple_dcp *dcp) memmove(r + off - 2, r + off, n - off); memset(r + n - 2, 0, 2); } +#endif } #else static void dcp_h17p_prepare_swap(struct apple_dcp *dcp) { } @@ -1561,7 +1688,12 @@ static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cooki static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) { struct dcp_set_parameter_dcp param = { - .param = IOMFBPARAM_ADAPTIVE_SYNC, + .param = +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + 15, +#else + IOMFBPARAM_ADAPTIVE_SYNC, +#endif .value = { 0 }, #if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) .count = 3, @@ -2105,6 +2237,10 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, struct dcp_wait_cookie *wait = cookie; if (wait) { +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + if (!data || get_unaligned_le32(data)) + wait->status = -EIO; +#endif complete(&wait->done); kref_put(&wait->refcount, release_wait_cookie); } @@ -2114,6 +2250,16 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, static void dcp_on_set_adaptive_sync(struct apple_dcp *dcp, void *out, void *cookie) { +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + if (!out || get_unaligned_le32(out)) { + struct dcp_wait_cookie *wait = cookie; + + wait->status = -EOPNOTSUPP; + complete(&wait->done); + kref_put(&wait->refcount, release_wait_cookie); + return; + } +#endif dcp_set_digital_out_mode(dcp, false, &dcp->mode, complete_set_digital_out_mode, cookie); } @@ -2122,7 +2268,12 @@ static void dcp_set_adaptive_sync(struct apple_dcp *dcp, u32 min_vrr, void *cookie) { struct dcp_set_parameter_dcp param = { - .param = IOMFBPARAM_ADAPTIVE_SYNC, + .param = +#if DCP_FW_VER >= DCP_FW_VERSION(27, 0, 0) + 15, +#else + IOMFBPARAM_ADAPTIVE_SYNC, +#endif .value = { min_vrr, /* minRR, 16.16 fixed-point Hz */ 0, /* mediaTargetRate */ diff --git a/drivers/gpu/drm/apple/iomfb_v27.c b/drivers/gpu/drm/apple/iomfb_v27.c new file mode 100644 index 0000000000000..f3f8f3bc88ace --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_v27.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright The Asahi Linux Contributors */ +/* Copyright 2026 Aurora Silicon */ + +#include +#include + +#include "dcp-link.h" +#include "iomfb_v27.h" +#include "iomfb_v12_3.h" +#include "iomfb_v13_3.h" +#include "version_utils.h" +#include "dcp-internal.h" + +void iomfb_serialize_present_v27(struct dcp_swap_submit_req_v27 *wire, + const struct dcp_swap_submit_req_h17p *request) +{ + memset(wire, 0, sizeof(*wire)); + memcpy(wire->swap.timestamp, request->swap.timestamp, + sizeof(wire->swap.timestamp)); + wire->swap.unk_38 = request->swap.unk_38; + /* The common geometry runs from swap_id through bg_color. */ + memcpy(&wire->swap.swap_id, &request->swap.swap_id, + offsetof(struct dcp_swap_v27, reserved_158) - + offsetof(struct dcp_swap_v27, swap_id)); + memcpy(wire->surf, request->surf, sizeof(wire->surf)); + memcpy(wire->surf_iova, request->surf_iova, + sizeof(*request) - + offsetof(struct dcp_swap_submit_req_h17p, surf_iova)); +} + +struct dcp_h17p_hotplug_request { + __le64 connected; + u8 tiled_display[0x4c]; + u8 tiled_display_null; + u8 padding[3]; +} __packed; + +struct dcp_h17p_swap_info_request { + u8 swap_info[0xe0]; + u8 swap_info_null; + u8 padding[3]; +} __packed; + +static_assert(sizeof(struct dcp_h17p_hotplug_request) == 0x58); +static_assert(sizeof(struct dcp_h17p_swap_info_request) == 0xe4); + +static_assert(sizeof(struct dc_swap_complete_resp_h17p) == 0x730); +static_assert(offsetof(struct dc_swap_complete_resp_h17p, swap_id) == 0); +static_assert(offsetof(struct dcp_swap_v27, brightness_update) == 0x354); +static_assert(offsetof(struct dcp_swap_v27, brightness_nits) == 0x35e); +static_assert(sizeof(struct dcp_swap_v27) == 0x6e0); + +static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { + IOMFB_METHOD("A000", dcpep_late_init_signal), + IOMFB_METHOD("A027", dcpep_setup_video_limits), + IOMFB_METHOD("A136", iomfbep_a131_pmu_service_matched), + IOMFB_METHOD("A137", iomfbep_a132_backlight_service_matched), + IOMFB_METHOD("A389", dcpep_set_create_dfb), + IOMFB_METHOD("A390", iomfbep_a358_vi_set_temperature_hint), + IOMFB_METHOD("A401", dcpep_start_signal), + IOMFB_METHOD("A406", dcpep_swap_start), + IOMFB_METHOD("A408", dcpep_swap_submit), + IOMFB_METHOD("A411", dcpep_set_display_device), + IOMFB_METHOD("A412", dcpep_is_main_display), + IOMFB_METHOD("A413", dcpep_set_digital_out_mode), + IOMFB_METHOD("A423", iomfbep_set_matrix), + IOMFB_METHOD("A427", iomfbep_get_color_remap_mode), + IOMFB_METHOD("A450", dcpep_set_parameter_dcp), + IOMFB_METHOD("A454", dcpep_create_default_fb), + IOMFB_METHOD("A037", dcpep_get_dfb_compression_info), + IOMFB_METHOD("A036", dcpep_get_dfb_info), + IOMFB_METHOD("A103", dcpep_get_dfb_layout), + IOMFB_METHOD("A453", dcpep_get_dfb_state), + IOMFB_METHOD("A104", dcpep_set_dfb_dimensions), + IOMFB_METHOD("A105", dcpep_commit_dfb_info), + IOMFB_METHOD("A035", dcpep_dfb_query), + IOMFB_METHOD("A384", dcpep_dfb_ready_query), + IOMFB_METHOD("A353", dcpep_pipe_query_353), + IOMFB_METHOD("A352", dcpep_pipe_set_352), + IOMFB_METHOD("A415", dcpep_pipe_cfg_415), + IOMFB_METHOD("A033", dcpep_pipe_cfg_031), + IOMFB_METHOD("A414", dcpep_pipe_cfg_414), + IOMFB_METHOD("A489", dcpep_pipe_query_478), + IOMFB_METHOD("A485", dcpep_pipe_query_474), + IOMFB_METHOD("A487", dcpep_pipe_set_476), + IOMFB_METHOD("A428", dcpep_pipe_set_428), + IOMFB_METHOD("A458", dcpep_enable_disable_video_power_savings), + IOMFB_METHOD("A466", dcpep_first_client_open), + IOMFB_METHOD("A467", iomfbep_last_client_close), + IOMFB_METHOD("A475", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A478", dcpep_flush_supports_power), + IOMFB_METHOD("A479", iomfbep_abort_swaps_dcp), + IOMFB_METHOD("A484", dcpep_set_power_state), + IOMFB_METHOD("A483", dcpep_register_dfb_surface), +}; + +#define iomfb_modeset_h17p iomfb_modeset_v27 +#define iomfb_flush_h17p iomfb_flush_v27 +#define iomfb_poweron_h17p iomfb_poweron_v27 +#define iomfb_poweroff_h17p iomfb_poweroff_v27 +#define iomfb_sleep_h17p iomfb_sleep_v27 +#define iomfb_shutdown_h17p iomfb_shutdown_v27 +#define iomfb_present_backlight_h17p iomfb_present_backlight_v27 +#define DCP_FW h17p +#define DCP_FW_VER DCP_FW_VERSION(27, 0, 0) + +#include "iomfb_template.c" + +static bool trampoline_rt_bandwidth_h17p(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + u64 scratch = dcp->disp_firmware_base[dcp->disp_bw_scratch_index] + + dcp->disp_bw_scratch_offset; + u64 clock_request = 0; + + trace_iomfb_callback(dcp, tag, __func__); + if (apple_dcp_h17p_rt_bw_encode_reply(in, out, scratch, clock_request)) + dev_warn(dcp->dev, "D003 bandwidth request is malformed\n"); + return true; +} + +static bool trampoline_get_frequency_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct apple_dcp_h17p_clock_request *request = in; + u32 index = le32_to_cpu(request->clock_id); + u64 rate = 0; + + if (index < dcp->num_firmware_clocks) + rate = clk_get_rate(dcp->firmware_clocks[index].clk); + else + dev_err(dcp->dev, "DCP requested undeclared provider clock index %u\n", + index); + *(__le64 *)out = cpu_to_le64(rate); + return true; +} + +static bool trampoline_hotplug_h17p(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct dcp_h17p_hotplug_request *request = in; + + if (!(request->tiled_display_null & 1)) + memcpy(out, request->tiled_display, + sizeof(request->tiled_display)); + return trampoline_hotplug(dcp, tag, out, in); +} + +static bool trampoline_swap_info_h17p(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct dcp_h17p_swap_info_request *request = in; + + trace_iomfb_callback(dcp, tag, __func__); + if (!(request->swap_info_null & 1)) + memcpy(out, request->swap_info, sizeof(request->swap_info)); + return true; +} + +static bool iomfbep_cb_video_range_h17p(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + __le32 *resp = out; + + resp[0] = cpu_to_le32(0x435b0000); + resp[1] = 0; + return true; +} + +struct dcp_h17p_get_data_prop_req { + char obj[4]; + char key[0x40]; + __le32 length; + u8 data_null; + u8 length_null; + u8 padding[2]; +} __packed; +static_assert(sizeof(struct dcp_h17p_get_data_prop_req) == 0x4c); + +struct dcp_h17p_get_data_prop_resp { + u8 data[0xc00]; + __le32 length; +} __packed; +static_assert(sizeof(struct dcp_h17p_get_data_prop_resp) == 0xc04); + +static bool trampoline_get_data_prop_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct dcp_h17p_get_data_prop_req *req = in; + struct dcp_h17p_get_data_prop_resp *resp = out; + const struct property *prop; + u32 cap = min_t(u32, le32_to_cpu(req->length), sizeof(resp->data)); + + if (memcmp(req->obj, "VORP", sizeof(req->obj)) || + strnlen(req->key, sizeof(req->key)) == sizeof(req->key)) + return true; + + prop = of_find_property(dcp->dev->of_node, req->key, NULL); + if (!prop || prop->length < 0 || prop->length > cap) + return true; + memcpy(resp->data, prop->value, prop->length); + resp->length = cpu_to_le32(prop->length); + return true; +} + +struct dcp_allocate_buffer_req_v27 { + __le32 options; + __le64 size; + __le32 alignment; + u8 dva_null; + u8 capacity_null; + u8 padding[2]; +} __packed; + +struct dcp_allocate_buffer_resp_v27 { + __le64 dva; + __le64 capacity; + __le32 mem_desc_id; +} __packed; + +struct dcp_buffer_lifecycle_req_v27 { + __le32 mem_desc_id; + __le32 options; +} __packed; + +static_assert(sizeof(struct dcp_allocate_buffer_req_v27) == 0x14); +static_assert(sizeof(struct dcp_allocate_buffer_resp_v27) == 0x14); +static_assert(offsetof(struct dcp_allocate_buffer_resp_v27, mem_desc_id) == 0x10); +static_assert(sizeof(struct dcp_buffer_lifecycle_req_v27) == 8); + +static bool trampoline_allocate_buffer_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct dcp_allocate_buffer_req_v27 *req = in; + struct dcp_allocate_buffer_resp_v27 *resp = out; + struct dcp_allocate_buffer_req allocation = { + .unk0 = le32_to_cpu(req->options), + .size = le64_to_cpu(req->size), + .unk2 = le32_to_cpu(req->alignment), + }; + struct dcp_allocate_buffer_resp buffer; + + if (!allocation.size || allocation.size > SIZE_MAX - SZ_16K || + allocation.unk2 > PAGE_SIZE || + (allocation.unk2 && !is_power_of_2(allocation.unk2))) + return true; + buffer = dcpep_cb_allocate_buffer(dcp, &allocation); + if (!req->dva_null) + resp->dva = cpu_to_le64(buffer.dva); + if (!req->capacity_null) + resp->capacity = cpu_to_le64(ALIGN(buffer.dva_size, SZ_16K)); + resp->mem_desc_id = cpu_to_le32(buffer.mem_desc_id); + return true; +} + +static bool trampoline_buffer_lifecycle_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct dcp_buffer_lifecycle_req_v27 *req = in; + __le32 *status = out; + u32 id = le32_to_cpu(req->mem_desc_id); + + if (!id || id >= DCP_MAX_MAPPINGS || + !test_bit(id, dcp->memdesc_map) || !dcp->memdesc[id].buf) { + *status = cpu_to_le32(14); + return true; + } + if (tag == 452) + dma_wmb(); + else + dma_rmb(); + *status = 0; + return true; +} + +static bool trampoline_ideal_screen_space_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const char *gamut; + u32 space = 1; + + if (!of_property_read_string(dcp->dev->of_node, + "apple,artwork-display-gamut", &gamut)) { + if (!strcmp(gamut, "P3")) + space = 2; + else if (!strcmp(gamut, "AdobeRGB")) + space = 3; + } + *(__le32 *)out = cpu_to_le32(space); + return true; +} + +static bool trampoline_tiling_state_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + const struct dcpep_get_tiling_state_req *req = in; + struct dcpep_get_tiling_state_resp *resp = out; + + resp->value = req->value; + resp->ret = 0; + if (!req->value_null && (req->param == 1 || req->param == 11) && + dcp->connector && !dcp->connector->base.has_tile) { + resp->value = 0; + resp->ret = 1; + } + return true; +} + +#include "iomfb-display-identity-v27.inc" +#include "iomfb-external-counts-v27.inc" +#include "iomfb-boolean-properties-v27.inc" + +static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { + [0] = dcpep_cb_d000_h17p, + [1] = trampoline_true, + [2] = trampoline_nop, + [3] = trampoline_rt_bandwidth_h17p, + [6] = trampoline_set_frame_sync_props_h17p, + [100] = iomfbep_cb_match_pmu_service, + [101] = trampoline_zero, + [102] = trampoline_nop, + [104] = trampoline_nop, + [106] = trampoline_nop, + [107] = trampoline_true, + [108] = trampoline_true, + [109] = trampoline_true, + [110] = trampoline_true, + [111] = trampoline_create_backlight_service, + [112] = trampoline_true, + [113] = trampoline_zero, + [116] = trampoline_nop, + [117] = trampoline_zero, + [118] = trampoline_zero, + [119] = trampoline_zero, + [114] = trampoline_tiling_state_v27, + [120] = dcpep_cb_boot_1, + [121] = trampoline_false, + [122] = trampoline_false, + [123] = trampoline_zero, + [124] = trampoline_read_edt_data, + [126] = trampoline_prop_start, + [127] = trampoline_prop_chunk, + [128] = trampoline_prop_end, + [129] = trampoline_allocate_bandwidth, + [130] = trampoline_update_external_counts_v27, + [131] = trampoline_get_external_counts_v27, + [132] = trampoline_apply_suppression_v27, + [133] = trampoline_display_identity_v27, + [201] = trampoline_map_piodma, + [202] = trampoline_unmap_piodma, + [206] = iomfbep_cb_match_pmu_service_2, + [207] = iomfbep_cb_match_backlight_service, + [204] = trampoline_ideal_screen_space_v27, + [208] = trampoline_nop, + [209] = trampoline_get_time, + [300] = trampoline_pr_publish, + [400] = trampoline_get_data_prop_v27, + [401] = trampoline_get_uint_prop, + [404] = trampoline_nop, + [406] = trampoline_set_fx_prop, + [408] = trampoline_get_frequency_v27, + [411] = trampoline_map_reg, + [413] = trampoline_true, + [414] = trampoline_sr_set_property_int, + [415] = trampoline_true, + [451] = trampoline_allocate_buffer_v27, + [452] = trampoline_buffer_lifecycle_v27, + [453] = trampoline_buffer_lifecycle_v27, + [454] = trampoline_release_mem_desc, + [552] = trampoline_true, + [561] = trampoline_true, + [563] = trampoline_true, + [565] = trampoline_true, + [567] = trampoline_true, + [569] = trampoline_set_boolean_property_v27, + [572] = trampoline_zero, + [574] = iomfbep_cb_video_range_h17p, + [575] = trampoline_hotplug_h17p, + [576] = trampoline_nop, + [586] = iomfbep_cb_create_dfb_surface, + [587] = trampoline_nop, + [588] = trampoline_nop, + [589] = trampoline_swap_info_h17p, + + [592] = trampoline_nop, + [594] = trampoline_swap_complete, + [596] = trampoline_swap_complete_intent_gated, + [597] = trampoline_abort_swap_ap_gated, + [598] = trampoline_enable_backlight_message_ap_gated, + [599] = trampoline_nop, + [601] = trampoline_false, + [602] = trampoline_false, + [603] = trampoline_nop, +}; + +static const struct { + u16 input, output; +} callback_sizes[IOMFB_MAX_CB] = { + [0] = { 0x0, 0x4 }, + [1] = { 0x0, 0x4 }, + [2] = { 0x0, 0x0 }, + [3] = { 0x4, 0x14 }, + [6] = { 0x54, 0x50 }, + [300] = { 0x10, 0x0 }, + [100] = { 0x0, 0x0 }, + [101] = { 0x0, 0x4 }, + [102] = { 0x44, 0x0 }, + [104] = { 0x44, 0x0 }, + [106] = { 0x40, 0x0 }, + [107] = { 0x0, 0x4 }, + [108] = { 0x0, 0x4 }, + [109] = { 0x0, 0x4 }, + [110] = { 0x0, 0x4 }, + [111] = { 0x0, 0x4 }, + [112] = { 0x0, 0x4 }, + [113] = { 0x1044, 0x1004 }, + [116] = { 0xc, 0x0 }, + [117] = { 0x14, 0x4 }, + [118] = { 0x3e88, 0x4 }, + [119] = { 0x4, 0x4 }, + [114] = { 0x10, 0x8 }, + [120] = { 0x0, 0x4 }, + [121] = { 0x0, 0x4 }, + [122] = { 0x0, 0x4 }, + [123] = { 0x0, 0x4 }, + [124] = { 0x64, 0x24 }, + [126] = { 0x4, 0x4 }, + [127] = { 0x1008, 0x4 }, + [128] = { 0x40, 0x4 }, + [129] = { 0x1c, 0x14 }, + [130] = { 0x8, 0x0 }, + [131] = { 0x10, 0xc }, + [132] = { 0x4, 0x0 }, + [133] = { 0x0, 0x4 }, + [201] = { 0xc, 0x10 }, + [202] = { 0x18, 0x0 }, + [206] = { 0x0, 0x4 }, + [207] = { 0x0, 0x4 }, + [204] = { 0x0, 0x4 }, + [208] = { 0x4, 0x0 }, + [209] = { 0x0, 0x8 }, + [400] = { 0x4c, 0xc04 }, + [401] = { 0x50, 0xc }, + [404] = { 0x48, 0x0 }, + [406] = { 0x48, 0x0 }, + [408] = { 0x8, 0x8 }, + [411] = { 0x10, 0x1c }, + [413] = { 0x1048, 0x4 }, + [414] = { 0x50, 0x4 }, + [415] = { 0x4c, 0x4 }, + [451] = { 0x14, 0x14 }, + [452] = { 0x8, 0x4 }, + [453] = { 0x8, 0x4 }, + [454] = { 0x4, 0x4 }, + [552] = { 0x1044, 0x4 }, + [561] = { 0x1044, 0x4 }, + [563] = { 0x4c, 0x4 }, + [565] = { 0x48, 0x4 }, + [567] = { 0x80, 0x4 }, + [569] = { 0x44, 0x4 }, + [572] = { 0x4, 0x4 }, + [574] = { 0x4, 0x8 }, + [575] = { 0x58, 0x4c }, + [576] = { 0x4, 0x0 }, + [586] = { 0x8, 0x4 }, + [587] = { 0x0, 0x0 }, + [588] = { 0x18, 0x0 }, + [589] = { 0xe4, 0xe0 }, + [592] = { 0x0, 0x0 }, + [594] = { 0x730, 0x0 }, + [596] = { 0x14, 0x0 }, + [597] = { 0x4, 0x0 }, + [598] = { 0x4, 0x0 }, + [599] = { 0x4, 0x0 }, + [601] = { 0x0, 0x4 }, + [602] = { 0x0, 0x4 }, + [603] = { 0x0, 0x0 }, +}; + +bool iomfb_v27_callback_size_valid(unsigned int tag, u32 input, u32 output) +{ + if (tag >= ARRAY_SIZE(callback_sizes) || !cb_handlers[tag]) + return false; + return input >= callback_sizes[tag].input && + output >= callback_sizes[tag].output; +} + +void iomfb_start_v27(struct apple_dcp *dcp) +{ + dcp->cb_handlers = cb_handlers; + dcp_start_signal(dcp, false, dcp_started, NULL); +} + +#undef DCP_FW_VER +#undef DCP_FW diff --git a/drivers/gpu/drm/apple/iomfb_v27.h b/drivers/gpu/drm/apple/iomfb_v27.h new file mode 100644 index 0000000000000..d59588d6193d5 --- /dev/null +++ b/drivers/gpu/drm/apple/iomfb_v27.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright The Asahi Linux Contributors */ +/* Copyright 2026 Aurora Silicon */ + +#ifndef APPLE_IOMFB_V27_H +#define APPLE_IOMFB_V27_H + +#include "iomfb_h17p.h" + +struct dcp_swap_v27 { + + u64 timestamp[7]; + u64 unk_38; + + u64 debug_info[2]; + + u64 event_wait[SWAP_SURFACES]; + u64 event_signal_on_glass; + u64 event_signal[SWAP_SURFACES]; + + u32 swap_id; + + u32 surf_ids[SWAP_SURFACES]; + struct dcp_rect src_rect[SWAP_SURFACES]; + u32 surf_flags[SWAP_SURFACES]; + u32 surf_unk[SWAP_SURFACES]; + struct dcp_rect dst_rect[SWAP_SURFACES]; + u32 swap_enabled; + u32 swap_completed; + + u32 bg_color; + u8 reserved_158[0x1fc]; + + u8 brightness_update; + u8 reserved_355[9]; + u64 brightness_nits; + u8 reserved_366[0x37a]; +} __packed; + +struct dcp_surface_v27 { + struct dcp_surface base; + u8 padding[47]; +} __packed; + +struct dcp_swap_submit_req_v27 { + struct dcp_swap_v27 swap; + struct dcp_surface_v27 surf[SWAP_SURFACES]; + u64 surf_iova[SWAP_SURFACES]; + u64 unk_u64_a[SWAP_SURFACES]; + + u8 unkbool; + u64 unkdouble; + u64 unkU64; + u8 unkbool2; + u32 clear; + u32 unkU32Ptr; + u8 swap_null; + u8 surf_null[SWAP_SURFACES]; + u8 unkoutbool_null; + u8 unknown_pointer_null; + u8 unknown_output_null; + u8 padding[2]; +} __packed; + +struct dcp_swap_submit_resp_v27 { + u8 unkoutbool; + u32 unkU32out; + u32 ret; + u8 padding[3]; +} __packed; + +static_assert(sizeof(struct dcp_swap_v27) == 0x6e0); +static_assert(sizeof(struct dcp_surface_v27) == 0x22c); +static_assert(sizeof(struct dcp_swap_submit_req_v27) == 0xff4); +static_assert(offsetof(struct dcp_swap_submit_req_v27, surf) == 0x6e0); +static_assert(offsetof(struct dcp_swap_submit_req_v27, surf_iova) == 0xf90); +static_assert(offsetof(struct dcp_swap_submit_req_v27, swap_null) == 0xfea); +static_assert(offsetof(struct dcp_swap_v27, brightness_update) == 0x354); +static_assert(offsetof(struct dcp_swap_v27, brightness_nits) == 0x35e); + +void iomfb_serialize_present_v27(struct dcp_swap_submit_req_v27 *wire, + const struct dcp_swap_submit_req_h17p *request); +bool iomfb_v27_callback_size_valid(unsigned int tag, u32 input, u32 output); +void iomfb_firmware_state_cleanup_v27(void *data); +int iomfb_modeset_v27(struct apple_dcp *dcp, struct drm_crtc_state *state); +void iomfb_flush_v27(struct apple_dcp *dcp, struct drm_crtc *crtc, + struct drm_atomic_state *state); +void iomfb_start_v27(struct apple_dcp *dcp); +void iomfb_shutdown_v27(struct apple_dcp *dcp); +void iomfb_poweron_v27(struct apple_dcp *dcp); +void iomfb_poweroff_v27(struct apple_dcp *dcp); +void iomfb_sleep_v27(struct apple_dcp *dcp); +void iomfb_present_backlight_v27(struct apple_dcp *dcp); + +#endif diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 16027c1923369..ebfcf78d2f543 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -735,6 +735,7 @@ int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, int ret = 0; struct iterator it; bool parsed_unit = false; + bool explicit_unit = false; bool parsed_name = false; bool parsed_class = false; @@ -782,10 +783,12 @@ int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, else parsed_class = true; } else if (!strcmp(key, "EPICUnit") || - (h17p_keys && !strcmp(key, "interface-id"))) { + (h17p_keys && !explicit_unit && !strcmp(key, "interface-id"))) { ret = parse_int(it.handle, unit); - if (!ret) + if (!ret) { parsed_unit = true; + explicit_unit |= !strcmp(key, "EPICUnit"); + } } else { ret = skip(it.handle); } diff --git a/drivers/gpu/drm/apple/systemep.c b/drivers/gpu/drm/apple/systemep.c index a32f0a0bcf6a0..ed468bee68d76 100644 --- a/drivers/gpu/drm/apple/systemep.c +++ b/drivers/gpu/drm/apple/systemep.c @@ -100,7 +100,7 @@ static int powerlog_report(struct apple_epic_service *service, enum epic_subtype dev_dbg(dcp->dev, "systemep: mNits event: Nits: %u.%03u, iDAC: %u\n", mnits.millinits / 1000, mnits.millinits % 1000, mnits.idac); - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if (dcp->fw_compat >= DCP_FIRMWARE_H17P && dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) { if (!dcp_has_panel(dcp)) return 0; diff --git a/tools/testing/selftests/aurora-apif/Makefile b/tools/testing/selftests/aurora-apif/Makefile index 711932711282e..1d64097501fbb 100644 --- a/tools/testing/selftests/aurora-apif/Makefile +++ b/tools/testing/selftests/aurora-apif/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -TEST_PROGS := afk.py apif_mmio.py pmp.py guest.py spmi.py rtc.py display.py centauri.py wifi.py bluetooth.py +TEST_PROGS := dcp.py afk.py apif_mmio.py pmp.py guest.py spmi.py rtc.py display.py centauri.py wifi.py bluetooth.py include ../lib.mk diff --git a/tools/testing/selftests/aurora-apif/dcp.py b/tools/testing/selftests/aurora-apif/dcp.py new file mode 100755 index 0000000000000..c2128bb033581 --- /dev/null +++ b/tools/testing/selftests/aurora-apif/dcp.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Validate the production DCP v27 serializer and framebuffer bounds.""" +from pathlib import Path +import re +import shutil +import subprocess +import tempfile +from pmp import block + +ROOT = Path(__file__).resolve().parents[4] +DRIVER = ROOT / "drivers/gpu/drm/apple" + + +def without_includes(source): + return re.sub(r"^#include .*\n", "", source, flags=re.M) + + +def main(): + print("TAP version 13\n1..1", flush=True) + if not shutil.which("cc"): + print("ok 1 - DCP firmware-27 wire and allocation bounds # SKIP cc unavailable") + return 4 + prolog = r""" +#include +#include +#include +#include +#include +#include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#define __packed __attribute__((packed)) +#define static_assert _Static_assert +#define SWAP_SURFACES 4 +#define DCP_FW_VER (26 << 16) +#define DCP_FW_VERSION(a,b,c) ((a << 16) | (b << 8) | c) +#define DCP_FW_NAME(name) name##_h17p +#define U32_MAX UINT32_MAX +#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1)) +#define DIV_ROUND_UP(v,a) (((v) + (a) - 1) / (a)) +#define fls64(v) ((v) ? 64 - __builtin_clzll(v) : 0) +#define ilog2(v) (63 - __builtin_clzll(v)) +#define roundup_pow_of_two(v) (1ULL << (64 - __builtin_clzll((v) - 1))) +struct apple_dcp; +struct drm_crtc; +struct drm_crtc_state; +struct drm_atomic_state; +""" + program = prolog + program += without_includes((DRIVER / "iomfb_plane.h").read_text()) + program += without_includes((DRIVER / "iomfb_template.h").read_text()) + program += without_includes((DRIVER / "iomfb_v27.h").read_text()) + program += without_includes((DRIVER / "iomfb-dfb.h").read_text()) + program += block((DRIVER / "iomfb_v27.c").read_text(), + r"^void iomfb_serialize_present_v27\(") + driver = (DRIVER / "iomfb_v27.c").read_text() + handlers = block(driver, r"^static const iomfb_cb_handler cb_handlers") + callback_sizes = block(driver, r"^static const struct \{\n\tu16 input, output;") + names = sorted(set(re.findall(r"= (\w+),", handlers))) + program += "\n#define IOMFB_MAX_CB 1000\n" + program += "#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0]))\n" + program += "typedef bool (*iomfb_cb_handler)(void);\n" + for name in names: + program += f"static bool {name}(void) {{ return true; }}\n" + program += handlers + ";\n" + callback_sizes + ";\n" + program += block(driver, r"^bool iomfb_v27_callback_size_valid\(") + program += r""" +int main(void) { + struct dcp_swap_submit_req_h17p input; + struct dcp_swap_submit_req_v27 wire; + memset(&input, 0xa5, sizeof(input)); + input.swap.swap_id = 0x12345678; + input.swap.swap_enabled = 0x40000005; + input.swap.bg_color = 0xfedcba98; + input.surf[0].base.format = 0x42475241; + input.surf_iova[0] = 0x123456789abULL; + input.unknown_output_null = 1; + memset(&wire, 0xcc, sizeof(wire)); + iomfb_serialize_present_v27(&wire, &input); + assert(sizeof(input) == 0xe9c && sizeof(wire) == 0xff4); + assert(wire.swap.swap_id == input.swap.swap_id); + assert(wire.swap.swap_enabled == input.swap.swap_enabled); + assert(wire.swap.bg_color == input.swap.bg_color); + assert(!memcmp(wire.swap.timestamp, input.swap.timestamp, 56)); + assert(!memcmp(wire.surf, input.surf, sizeof(wire.surf))); + assert(wire.surf_iova[0] == input.surf_iova[0]); + assert(wire.unknown_output_null == 1); + assert(wire.swap.debug_info[0] == 0 && wire.swap.event_wait[3] == 0); + assert(wire.swap.event_signal_on_glass == 0 && wire.swap.event_signal[3] == 0); + assert(wire.swap.brightness_update == 0 && wire.swap.brightness_nits == 0); + assert(wire.padding[0] == 0 && wire.padding[1] == 0); + u32 format, allocation; + assert(!apple_dcp_dfb_allocation(1920,1080,0x42475241,0,1ULL<<32, + &format,&allocation)); + assert(format == 0x42475241 && allocation == ALIGN(7680ULL * 1080,0x4000)); + assert(apple_dcp_dfb_allocation(1920,1080,0x42475241,0,0x4000, + &format,&allocation) == -ENOSPC); + assert(apple_dcp_dfb_allocation(0,1080,0x42475241,0,1ULL<<32, + &format,&allocation) == -EINVAL); + assert(apple_dcp_dfb_allocation(16385,1080,0x42475241,0,1ULL<<32, + &format,&allocation) == -EINVAL); + assert(apple_dcp_dfb_allocation(1920,1080,0xdeadbeef,0,1ULL<<32, + &format,&allocation) == -EOPNOTSUPP); + assert(apple_dcp_dfb_allocation(1920,1080,0x42475241,1,1ULL<<32, + &format,&allocation) == -EOPNOTSUPP); + assert(!apple_dcp_dfb_allocation(3840,2160,0x62336138,2,1ULL<<32, + &format,&allocation)); + assert(format == 0x77343061 && allocation % 0x4000 == 0); + assert(apple_dcp_dfb_round_size((1ULL<<24)+1) == (1ULL<<24)); + assert(apple_dcp_dfb_round_size((1ULL<<24)+3) == (1ULL<<24)+4); + assert(iomfb_v27_callback_size_valid(594, 0x730, 0)); + assert(!iomfb_v27_callback_size_valid(594, 0x72f, 0)); + assert(iomfb_v27_callback_size_valid(451, 0x14, 0x14)); + assert(!iomfb_v27_callback_size_valid(451, 0x13, 0x14)); + assert(!iomfb_v27_callback_size_valid(451, 0x14, 0x13)); + assert(!iomfb_v27_callback_size_valid(999, 0xffff, 0xffff)); + assert(!iomfb_v27_callback_size_valid(1000, 0xffff, 0xffff)); + return 0; +} +""" + with tempfile.TemporaryDirectory() as tmp: + source, binary = Path(tmp) / "dcp.c", Path(tmp) / "dcp" + source.write_text(program) + subprocess.run(["cc", "-std=gnu11", "-Wall", "-Wextra", "-Werror", + "-fsanitize=address,undefined", str(source), "-o", str(binary)], + check=True) + subprocess.run([str(binary)], check=True) + print("ok 1 - DCP firmware-27 serializer and framebuffer bounds") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())