Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/gpu/drm/apple/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ appledrm-y += iomfb_v14_7.o iomfb_v14_7_link.o
appledrm-$(CONFIG_DRM_APPLE_DCP_T8132) += iomfb_m4.o t8132_ext_drm.o
appledrm-y += iomfb_h17p.o
appledrm-$(CONFIG_DRM_APPLE_KUNIT_TEST) += iomfb_h17p_test.o parser_test.o
appledrm-$(CONFIG_DRM_APPLE_KUNIT_TEST) += iomfb_v27_test.o
appledrm-$(CONFIG_DRM_APPLE_KUNIT_TEST) += iomfb_external_hdr_hints_test.o
appledrm-$(CONFIG_DRM_APPLE_KUNIT_TEST) += apple_resume_test.o
appledrm-$(CONFIG_DRM_APPLE_KUNIT_TEST) += apple_boot_display_test.o
Expand Down
95 changes: 66 additions & 29 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,10 @@ bool dcp_t6041_ext_enabled(void)
#include "dcp-peer-probe-v27.inc"

/*
* The backlight maximum of a desktop 26.x panel whose node has no
* apple,max-brightness: the J616s panel's user-accessible SDR maximum (ADT
* user-accessible-max-nits, 0x02580000 in 16.16).
* The backlight maximum of a T6041 panel whose node has no
* apple,max-brightness: the J616s panel's user-accessible SDR maximum.
*/
#define DCP_DESKTOP_MAX_NITS 600
#define DCP_T6041_MAX_NITS 600

static bool dcp_desktop_external(struct apple_dcp *dcp)
{
Expand Down Expand Up @@ -5293,14 +5292,45 @@ static int dcp_read_fw_version(struct device *dev, const char *name,
return len;
}

enum dcp_firmware_version dcp_t6041_firmware_version(const char *firmware,
const char *compat)
{
/* Firmware 27 is selected by its compatibility ABI, as on T6050. */
if (!strcmp(compat, "27.2.0"))
return DCP_FIRMWARE_V_27_0;
if (!strcmp(firmware, compat) &&
(!strcmp(firmware, "26.5.2") || !strcmp(firmware, "26.6.2")))
return DCP_FIRMWARE_V_26_6_DESKTOP;
return DCP_FIRMWARE_UNKNOWN;
}

static bool dcp_v27_method_table_supported(struct device *dev,
const char *compat)
{
const struct dcp_method_entry *methods = iomfb_v27_methods(compat);
unsigned int num_missing;
const char *missing;

if (!methods) {
dev_err(dev, "DCP firmware-compat %s has no 27.x method table\n",
compat);
return false;
}
missing = iomfb_v27_missing_method(methods, &num_missing);
if (missing) {
dev_err(dev, "DCP firmware-compat %s not started: no ID known for %u methods (first: %s)\n",
compat, num_missing, missing);
return false;
}
return true;
}

static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev)
{
const struct apple_dcp_hw_data *hw = of_device_get_match_data(dev);
const struct dcp_v14_board *v14_board;
char compat_str[DCP_FW_VERSION_STR_LEN];
char fw_str[DCP_FW_VERSION_STR_LEN];
unsigned int num_missing;
const char *missing;
int ret;

/*
Expand All @@ -5317,18 +5347,12 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev)

if (hw->firmware_compat == DCP_FIRMWARE_V_27_0) {
ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str);
if (ret < 0 || !iomfb_v27_methods(compat_str)) {
dev_err(dev, "DCP firmware-compat %s has no 27.x method table\n",
ret < 0 ? "missing" : compat_str);
if (ret < 0) {
dev_err(dev, "DCP firmware-compat missing has no 27.x method table\n");
return DCP_FIRMWARE_UNKNOWN;
}
missing = iomfb_v27_missing_method(iomfb_v27_methods(compat_str),
&num_missing);
if (missing) {
dev_err(dev, "DCP firmware-compat %s not started: no ID known for %u methods (first: %s)\n",
compat_str, num_missing, missing);
if (!dcp_v27_method_table_supported(dev, compat_str))
return DCP_FIRMWARE_UNKNOWN;
}
return DCP_FIRMWARE_V_27_0;
}

Expand All @@ -5351,11 +5375,17 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev)
of_device_is_compatible(dev->of_node, "apple,t6041-dcpext")) {
int compat_ret = dcp_read_fw_version(dev, "apple,firmware-compat",
compat_str);

if (ret >= 0 && compat_ret >= 0 && !strcmp(fw_str, compat_str) &&
(!strcmp(fw_str, "26.5.2") || !strcmp(fw_str, "26.6.2")))
return DCP_FIRMWARE_V_26_6_DESKTOP;
dev_err(dev, "T6041 display not started: DCP firmware %s (compat %s) is not 26.5.2 or 26.6.2\n",
enum dcp_firmware_version version;

if (ret >= 0 && compat_ret >= 0) {
version = dcp_t6041_firmware_version(fw_str, compat_str);
if (version == DCP_FIRMWARE_V_27_0 &&
!dcp_v27_method_table_supported(dev, compat_str))
return DCP_FIRMWARE_UNKNOWN;
if (version != DCP_FIRMWARE_UNKNOWN)
return version;
}
dev_err(dev, "T6041 display not started: unsupported DCP firmware %s (compat %s)\n",
ret >= 0 ? fw_str : "missing",
compat_ret >= 0 ? compat_str : "missing");
return DCP_FIRMWARE_UNKNOWN;
Expand Down Expand Up @@ -5812,16 +5842,18 @@ static int dcp_comp_bind_pipe(struct device *dev, struct device *main, void *dat
panel_np = of_get_compatible_child(dev->of_node, "apple,panel");

if (panel_np) {
if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) {
if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP ||
(dcp->fw_compat == DCP_FIRMWARE_V_27_0 &&
of_device_is_compatible(dcp->dev->of_node, "apple,t6041-dcp"))) {
/*
* Desktop 26.x swaps carry nits, and the panel node
* describes the panel whether or not the boot loader
* enabled it. Without apple,max-brightness take the
* J616s panel's user-accessible SDR maximum.
* The T6041 panel node has no apple,max-brightness in
* either firmware lineage. Use its user-accessible SDR
* maximum; firmware 27 takes the peak and EDR headroom
* from ADT below.
*/
if (of_property_read_u32(panel_np, "apple,max-brightness",
&dcp->brightness.maximum))
dcp->brightness.maximum = DCP_DESKTOP_MAX_NITS;
dcp->brightness.maximum = DCP_T6041_MAX_NITS;
} else if (of_device_is_available(panel_np)) {
ret = of_property_read_u32(panel_np, "apple,max-brightness",
&dcp->brightness.maximum);
Expand All @@ -5831,7 +5863,7 @@ static int dcp_comp_bind_pipe(struct device *dev, struct device *main, void *dat

dcp_panel_read_dimensions(dcp, panel_np);
of_node_put(panel_np);
/* Read target metadata for H17P and desktop firmware alike. */
/* Read target metadata for firmware 27 and desktop firmware alike. */
dcp_panel_read_adt(dcp);
dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP;
dcp->connector_type = DRM_MODE_CONNECTOR_eDP;
Expand Down Expand Up @@ -6401,7 +6433,7 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev)
fw_compat = dcp_check_firmware_version(dev);
if (fw_compat == DCP_FIRMWARE_UNKNOWN)
return -ENODEV;
if (fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) {
if (of_device_is_compatible(dev->of_node, "apple,t6041-dcp")) {
ret = dcp_validate_t6041_dt(dev);
if (ret)
return ret;
Expand Down Expand Up @@ -6500,6 +6532,11 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev)
if (ret)
return ret;
dcp->hw = *(struct apple_dcp_hw_data *)of_device_get_match_data(dev);
/* T6041 firmware 27 is the inherited live-session path, like T6050. */
if (fw_compat == DCP_FIRMWARE_V_27_0 &&
(of_device_is_compatible(dev->of_node, "apple,t6041-dcp") ||
of_device_is_compatible(dev->of_node, "apple,t6041-dcpext")))
dcp->hw.adopt_live_session = true;
/*
* T8142 on firmware 27 runs as the T6050's firmware-27 instance, with
* the default method profile: with H17G every swap fails with status 4.
Expand Down Expand Up @@ -8877,7 +8914,7 @@ static const struct apple_dcp_hw_data apple_dcp_hw_t8132_dcpext = {
};
#endif

/* T6041 (M4 Pro/Max), desktop 26.6 firmware */
/* T6041 (M4 Pro/Max), selected at probe between desktop 26.x and firmware 27. */
static const struct apple_dcp_hw_data apple_dcp_hw_t6041 = {
.num_dptx_ports = 2,
};
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/apple/dcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ bool dcp_fw_compat_is_14_7(struct platform_device *pdev);
bool dcp_fw_compat_is_desktop(struct platform_device *pdev);
bool dcp_fw_compat_panel_edr(struct platform_device *pdev);
unsigned int dcp_gamma_lut_size(struct platform_device *pdev);
enum dcp_firmware_version dcp_t6041_firmware_version(const char *firmware,
const char *compat);
/* T6041 external display processors may join the DRM device. */
bool dcp_t6041_ext_enabled(void);
void dcp_backlight_takeover_registered(struct platform_device *pdev);
Expand Down
14 changes: 11 additions & 3 deletions drivers/gpu/drm/apple/iomfb_v27.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ static const struct dcp_method_entry dcp_methods_27_0[dcpep_num_methods] = {
* A400/A401, so IOMobileFramebufferAP moved up by 50 (start_signal A451,
* get_color_remap_mode A477), and set_create_DFB and vi_set_temperature_hint
* up by one. The UPPipe A0xx and A1xx IDs are unchanged. The firmware rejects
* an unknown tag with a panic ("link_rpc_lookup callback not found"), so
* dfb_ready_query, whose 27.2 tag has not been seen in a call, has no entry
* here and is not sent to this build (boot_5()).
* an unknown tag with a panic ("link_rpc_lookup callback not found").
* dfb_ready_query is A385, one above its 27.0 ID: the 27.3 trace sends it
* there, and the J813's DCP answers it at A385 on 27.2.
*/
static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = {
IOMFB_METHOD("A000", dcpep_late_init_signal),
Expand All @@ -147,6 +147,7 @@ static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = {
IOMFB_METHOD("A104", dcpep_set_dfb_dimensions),
IOMFB_METHOD("A105", dcpep_commit_dfb_info),
IOMFB_METHOD("A035", dcpep_dfb_query),
IOMFB_METHOD("A385", dcpep_dfb_ready_query),
IOMFB_METHOD("A353", dcpep_pipe_query_353),
IOMFB_METHOD("A352", dcpep_pipe_set_352),
IOMFB_METHOD("A465", dcpep_pipe_cfg_415),
Expand Down Expand Up @@ -601,6 +602,12 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = {
[587] = trampoline_nop,
[588] = trampoline_nop,
[589] = trampoline_swap_info_h17p,
/*
* 27.2 sends this 20-byte notification with no reply after swaps (the
* macOS handler next to swap_info is the buffer-release notification);
* nothing here consumes it.
*/
[590] = trampoline_nop,

[592] = trampoline_nop,
[594] = trampoline_swap_complete,
Expand Down Expand Up @@ -687,6 +694,7 @@ static const struct {
[587] = { 0x0, 0x0 },
[588] = { 0x18, 0x0 },
[589] = { 0xe4, 0xe0 },
[590] = { 0x14, 0x0 },
[592] = { 0x0, 0x0 },
[594] = { 0x730, 0x0 },
[596] = { 0x14, 0x0 },
Expand Down
69 changes: 69 additions & 0 deletions drivers/gpu/drm/apple/iomfb_v27_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2026 Aurora Silicon */

#include <kunit/test.h>
#include <linux/module.h>

#include "dcp.h"
#include "iomfb.h"
#include "iomfb_v27.h"

static void v27_method_table_test(struct kunit *test)
{
const struct dcp_method_entry *methods;
unsigned int missing;

methods = iomfb_v27_methods("27.2.0");
KUNIT_ASSERT_NOT_NULL(test, methods);
KUNIT_EXPECT_MEMEQ(test, methods[dcpep_dfb_ready_query].tag, "A385", 4);
KUNIT_EXPECT_PTR_EQ(test, iomfb_v27_missing_method(methods, &missing), NULL);
KUNIT_EXPECT_EQ(test, missing, 0U);
KUNIT_EXPECT_PTR_EQ(test, iomfb_v27_methods("27.1.0"), NULL);
}

static void v27_d590_callback_test(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, iomfb_v27_callback_size_valid(590, 20, 0));
KUNIT_EXPECT_FALSE(test, iomfb_v27_callback_size_valid(590, 19, 0));
}

static void t6041_firmware_admission_test(struct kunit *test)
{
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("26.5.2", "26.5.2"),
DCP_FIRMWARE_V_26_6_DESKTOP);
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("26.6.2", "26.6.2"),
DCP_FIRMWARE_V_26_6_DESKTOP);
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("27.2.0", "27.2.0"),
DCP_FIRMWARE_V_27_0);
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("27.2.1", "27.2.0"),
DCP_FIRMWARE_V_27_0);
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("26.6.2", "27.2.0"),
DCP_FIRMWARE_V_27_0);
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("27.0.0", "27.0.0"),
DCP_FIRMWARE_UNKNOWN);
KUNIT_EXPECT_EQ(test,
dcp_t6041_firmware_version("26.6.2", "26.5.2"),
DCP_FIRMWARE_UNKNOWN);
}

static struct kunit_case v27_cases[] = {
KUNIT_CASE(v27_method_table_test),
KUNIT_CASE(v27_d590_callback_test),
KUNIT_CASE(t6041_firmware_admission_test),
{}
};

static struct kunit_suite v27_suite = {
.name = "apple-dcp-v27",
.test_cases = v27_cases,
};

kunit_test_suite(v27_suite);

MODULE_LICENSE("Dual MIT/GPL");
3 changes: 2 additions & 1 deletion drivers/soc/apple/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ config APPLE_DISPLAY_GATE

On T6041 (M4 Pro) device trees it enables the internal display nodes
only when the boot loader has handed the running display coprocessor
over (apple,t6041-handoff, 26.6.2 display firmware), and powers up
over (apple,t6041-handoff, qualified 26.6.2 or 27.2 display firmware),
and powers up
the J616s external display chain (HDMI, Type-C DisplayPort) once the
boot loader has given the external display processors their
firmware memory. Without these, or with nomodeset, it does nothing.
Expand Down
14 changes: 14 additions & 0 deletions drivers/soc/apple/display-gate-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1014,8 +1014,22 @@ static void __init gate_t6041_staged_steps(struct kunit *test)
"step %u", i);
}

static void __init gate_t6041_firmware_compat_test(struct kunit *test)
{
const u32 desktop[] = { 26, 6, 2 };
const u32 v27[] = { 27, 2, 0 };
const u32 old[] = { 26, 5, 2 };
const u32 unknown[] = { 27, 3, 0 };

KUNIT_EXPECT_TRUE(test, gate_t6041_firmware_compat(desktop));
KUNIT_EXPECT_TRUE(test, gate_t6041_firmware_compat(v27));
KUNIT_EXPECT_FALSE(test, gate_t6041_firmware_compat(old));
KUNIT_EXPECT_FALSE(test, gate_t6041_firmware_compat(unknown));
}

static struct kunit_case gate_t6041_cases[] __refdata = {
KUNIT_CASE(gate_t6041_staged_steps),
KUNIT_CASE(gate_t6041_firmware_compat_test),
{}
};

Expand Down
18 changes: 12 additions & 6 deletions drivers/soc/apple/display-gate.c
Original file line number Diff line number Diff line change
Expand Up @@ -2092,10 +2092,10 @@ static void __init gate_run(void)
/*
* T6041 internal display. The handoff describes the DCP that iBoot left
* running: the DCP, the display subsystem and the DCP's piodma child carry
* the marker, the DCP names the 26.6.2 firmware the desktop IOMFB instance is
* written for, and the DCP, display and piodma streams sit on the expected
* DARTs. The display nodes are enabled only when all of that checks out and
* the DCP is running.
* the marker, the DCP names a qualified 26.6.2 or 27.2 firmware interface,
* and the DCP, display and piodma streams sit on the expected DARTs. The
* display nodes are enabled only when all of that checks out and the DCP is
* running.
*/
#define T6041_DCP_SID 23
#define T6041_DISP_SID 0
Expand All @@ -2115,6 +2115,12 @@ static const int t6041_enable[] __initconst = { T6041_DCP_DART, T6041_DISP_DART,
/* Applied, it stays: the live tree refers to it. */
static struct of_changeset t6041_gate;

static bool __init gate_t6041_firmware_compat(const u32 version[3])
{
return (version[0] == 26 && version[1] == 6 && version[2] == 2) ||
(version[0] == 27 && version[1] == 2 && version[2] == 0);
}

static void __init gate_t6041_internal(void)
{
struct device_node *nodes[T6041_NR_NODES] = {};
Expand All @@ -2139,10 +2145,10 @@ static void __init gate_t6041_internal(void)
!gate_marked(nodes[T6041_PIODMA]))
goto out;

/* The desktop IOMFB instance is written for the 26.6.2 firmware only. */
/* Admit the transition desktop ABI and the firmware-27 ABI. */
if (of_property_count_u32_elems(nodes[T6041_DCP], "apple,firmware-compat") != 3 ||
of_property_read_u32_array(nodes[T6041_DCP], "apple,firmware-compat", version, 3) ||
version[0] != 26 || version[1] != 6 || version[2] != 2)
!gate_t6041_firmware_compat(version))
goto out;

nodes[T6041_DCP_DART] = gate_target(nodes[T6041_DCP], "iommus", "#iommu-cells",
Expand Down
Loading