Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ select:
enum:
- apple,dcp
- apple,dcpext
- apple,t6041-dcpext
typec-routes: true
required:
- compatible
Expand Down Expand Up @@ -144,8 +145,14 @@ examples:
- |
#include <dt-bindings/phy/phy.h>

dcp {
dcp@10000000 {
compatible = "apple,t6020-dcpext", "apple,dcpext";
reg = <0x10000000 0x4000>, <0x11000000 0x4000>;
reg-names = "coproc", "disp-0";
mboxes = <&mbox>;
mbox-names = "mbox";
iommus = <&dart 0>;
apple,bw-scratch = <&pmgr 0 0 0>;
phys = <&atcphy0 PHY_TYPE_DP>;
phy-names = "typec0";
mux-controls = <&atcphy0_xbar 0>, <&atcphy0_xbar 1>,
Expand Down
31 changes: 30 additions & 1 deletion Documentation/devicetree/bindings/display/apple,dcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
description: Die containing the DisplayPort transmitter.

apple,t6041-ext-handoff:
$ref: /schemas/types.yaml#/definitions/uint32
const: 1
description: >
Set by the boot loader only after validating and reserving a T6041
external DCP's firmware and display DART mappings. The driver refuses
the external processor without this handoff.

apple,notch-height:
$ref: /schemas/types.yaml#/definitions/uint32
description: Height in pixels reserved for the display notch.
Expand Down Expand Up @@ -362,7 +370,10 @@ allOf:
properties:
compatible:
contains:
const: apple,t6030-dcpext
enum:
- apple,t6020-dcpext
- apple,t6030-dcpext
- apple,t6041-dcpext
required:
- compatible
else:
Expand Down Expand Up @@ -391,6 +402,24 @@ allOf:
then:
required:
- apple,bw-scratch
- if:
properties:
compatible:
contains:
const: apple,t6041-dcpext
required:
- compatible
then:
if:
properties:
status:
const: disabled
required:
- status
else:
required:
- apple,t6041-ext-handoff
- memory-region
- if:
not:
properties:
Expand Down
78 changes: 78 additions & 0 deletions arch/arm64/boot/dts/apple/t6040-j616s.dts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
bluetooth0 = "/pcie@1cb0000000/pci@0,0/bluetooth@0,1";
/* used by the loader to hand over the display */
dcp = &dcp;
dcpext0 = &dcpext0;
dcpext1 = &dcpext1;
disp0 = &display;
disp0-piodma = &disp0_piodma;
/* the boot loader finds the GPU node through this alias */
Expand Down Expand Up @@ -4150,6 +4152,14 @@
remote-endpoint = <&atcphy0_ss>;
};
};

port@3 {
reg = <3>;
status = "disabled";
typec0_display: endpoint {
remote-endpoint = <&dcpext1_typec0>;
};
};
};
};
};
Expand Down Expand Up @@ -4186,6 +4196,14 @@
remote-endpoint = <&atcphy1_ss>;
};
};

port@3 {
reg = <3>;
status = "disabled";
typec1_display: endpoint {
remote-endpoint = <&dcpext1_typec1>;
};
};
};
};
};
Expand Down Expand Up @@ -4237,6 +4255,14 @@
remote-endpoint = <&atcphy2_ss>;
};
};

port@3 {
reg = <3>;
status = "disabled";
typec2_display: endpoint {
remote-endpoint = <&dcpext1_typec2>;
};
};
};
};
};
Expand Down Expand Up @@ -4395,6 +4421,58 @@
};
};

/*
* ADT routes dcpext1 (UFP endpoint 1) to the three Type-C DPPHY outputs.
* The graph remains disabled until the loader hands the external firmware and
* display DARTs over and the Type-C DP mode switches can be activated.
*/
&dcpext1 {
apple,connector-type = "USB-C";
phys = <&atcphy0 PHY_TYPE_DP>, <&atcphy1 PHY_TYPE_DP>,
<&atcphy2 PHY_TYPE_DP>;
phy-names = "typec0", "typec1", "typec2";
mux-controls = <&atcphy0_xbar 0>, <&atcphy1_xbar 0>,
<&atcphy2_xbar 0>;
mux-control-names = "typec0", "typec1", "typec2";
apple,typec-mux-indices = <2 2 2>;
apple,typec-dptx-phys = <0 1 2>;

typec-routes {
#address-cells = <1>;
#size-cells = <0>;

route@0 {
reg = <0>;
mode-switch;
port {
dcpext1_typec0: endpoint {
remote-endpoint = <&typec0_display>;
};
};
};

route@1 {
reg = <1>;
mode-switch;
port {
dcpext1_typec1: endpoint {
remote-endpoint = <&typec1_display>;
};
};
};

route@2 {
reg = <2>;
mode-switch;
port {
dcpext1_typec2: endpoint {
remote-endpoint = <&typec2_display>;
};
};
};
};
};

/* Additional Abbey PMICs on the two independent NUB SPMI buses. */
&nub_spmi1 {
status = "okay";
Expand Down
30 changes: 27 additions & 3 deletions drivers/gpu/drm/apple/dcp-fabric.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/mux/driver.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/phy/phy.h>
#include <linux/slab.h>
#include <linux/soc/apple/dp-tunnel.h>
#include <linux/usb/typec_altmode.h>
Expand Down Expand Up @@ -102,9 +103,9 @@ static bool dcp_hdmi_borrowed(void *ctx);

bool dcp_is_typec_output(struct apple_dcp *dcp)
{
/* Desktop 26.x keeps its own Type-C route, outside this fabric. */
/* Desktop 26.x only has a Type-C output while a route owns a port. */
if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP)
return false;
return !!dcp->active_typec_route;
return dcp->active_typec_route ||
dcp->fixed_connector_type == DRM_MODE_CONNECTOR_USB;
}
Expand Down Expand Up @@ -367,6 +368,21 @@ static int dcp_typec_route_activate(struct apple_dcp_typec_route *route,
return ret;
}

if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP &&
xbar == route->xbar) {
ret = phy_init(route->phy);
if (!ret) {
ret = phy_power_on(route->phy);
if (ret)
phy_exit(route->phy);
}
if (ret) {
mux_control_deselect(route->xbar);
return ret;
}
route->desktop_phy_up = true;
}

dcp_modes_begin_attachment(dcp);
apple_connector_edid_set_live(dcp->connector, false);
dcp->phy = route->phy;
Expand Down Expand Up @@ -484,6 +500,11 @@ static int dcp_typec_route_deactivate(struct apple_dcp_typec_route *route)
}
if (dcp_is_external_h17p(dcp))
dpavservep_detach(dcp);
if (route->desktop_phy_up) {
phy_power_off(route->phy);
phy_exit(route->phy);
route->desktop_phy_up = false;
}
route->selected = false;
ended_attachment = dcp_modes_end_typec(dcp, route);
if (dcp->active_typec_route == route)
Expand Down Expand Up @@ -545,6 +566,9 @@ static int dcp_typec_route_deactivate(struct apple_dcp_typec_route *route)
dcp->phy = dcp->typec_routes[0].phy;
dcp->dptx_phy = dcp->typec_routes[0].dptx_phy;
}
} else if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) {
dcp->phy = NULL;
dcp->connector_type = DRM_MODE_CONNECTOR_USB;
} else {
/* Keep DPTX endpoint discovery working before a cable is attached. */
dcp->phy = dcp->typec_routes[0].phy;
Expand Down Expand Up @@ -3593,7 +3617,7 @@ static int dcp_register_typec_routes_present(void *data)
if (ret)
return ret;

if (!dcp->phy)
if (!dcp->phy && dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP)
dcp->phy = route->phy;
dcp->nr_typec_routes++;
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/apple/dcp-fabric.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ struct apple_dcp_typec_route {
u32 dptx_phy;
u32 mux_index;
bool selected;
/* Desktop T6041 powers a direct DP PHY while this route owns it. */
bool desktop_phy_up;
/* crossbar output actually selected: xbar (dpphy) or a Thunderbolt dpin */
unsigned int tunnel_dpin;
u64 tunnel_generation;
Expand Down
64 changes: 53 additions & 11 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,33 +247,75 @@ static bool dcp_desktop_external(struct apple_dcp *dcp)
}

/*
* The register map of the T6041 internal DCP, from the macOS ADT capture
* (/arm-io/dcp reg[0], /arm-io/disp0 reg[0..3]). The firmware maps these
* windows by index, so the device tree must describe exactly these.
* The T6041 display processors map these windows by index. Check each
* processor against its own register map before touching the coprocessor.
*/
static const struct {
struct t6041_dcp_reg {
const char *name;
u64 start;
u64 size;
} t6041_dcp_regs[] = {
};

static const struct t6041_dcp_reg t6041_dcp_regs[] = {
{ "coproc", 0x402e00000ULL, 0x4000 },
{ "disp-0", 0x400000000ULL, 0x4000000 },
{ "disp-1", 0x402240000ULL, 0x10000 },
{ "disp-2", 0x4023a8000ULL, 0x4000 },
{ "disp-3", 0x402800000ULL, 0x800000 },
};

static const struct t6041_dcp_reg t6041_dcpext0_regs[] = {
{ "coproc", 0x3a2e00000ULL, 0x4000 },
{ "disp-0", 0x3a0000000ULL, 0x4000000 },
{ "disp-1", 0x3a2240000ULL, 0x10000 },
{ "disp-2", 0x3a23a8000ULL, 0x4000 },
{ "disp-3", 0x3a2800000ULL, 0x800000 },
};

static const struct t6041_dcp_reg t6041_dcpext1_regs[] = {
{ "coproc", 0x602e00000ULL, 0x4000 },
{ "disp-0", 0x600000000ULL, 0x4000000 },
{ "disp-1", 0x602240000ULL, 0x10000 },
{ "disp-2", 0x6023a8000ULL, 0x4000 },
{ "disp-3", 0x602800000ULL, 0x800000 },
};

/* Refuse to touch the coprocessor on any mismatch with the captured ADT. */
static int dcp_validate_t6041_dt(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct device_node *piodma __free(device_node) =
of_get_child_by_name(dev->of_node, "piodma");
of_get_child_by_name(dev->of_node, "piodma");
const struct t6041_dcp_reg *regs = t6041_dcp_regs;
struct resource *coproc;
u32 handoff;
int i;

if (!of_device_is_compatible(dev->of_node, "apple,t6041-dcp"))
if (!of_device_is_compatible(dev->of_node, "apple,t6041-dcp") &&
!of_device_is_compatible(dev->of_node, "apple,t6041-dcpext"))
return 0;

coproc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!coproc)
return -ENODEV;
if (of_device_is_compatible(dev->of_node, "apple,t6041-dcpext")) {
/* Only the loader may grant the external firmware/DART handoff. */
if (of_property_read_u32(dev->of_node, "apple,t6041-ext-handoff",
&handoff) || handoff != 1)
return dev_err_probe(dev, -ENODEV,
"T6041 external DCP has no validated handoff\n");
if (coproc->start == t6041_dcpext0_regs[0].start)
regs = t6041_dcpext0_regs;
else if (coproc->start == t6041_dcpext1_regs[0].start)
regs = t6041_dcpext1_regs;
else
return dev_err_probe(dev, -ENODEV,
"Unknown T6041 external DCP register map\n");
} else if (coproc->start != t6041_dcp_regs[0].start) {
return dev_err_probe(dev, -ENODEV,
"Unknown T6041 internal DCP register map\n");
}

if (pdev->num_resources != ARRAY_SIZE(t6041_dcp_regs))
return dev_err_probe(dev, -ENODEV,
"T6041 DCP: %u register windows, the ADT has %zu\n",
Expand All @@ -282,13 +324,13 @@ static int dcp_validate_t6041_dt(struct device *dev)
for (i = 0; i < ARRAY_SIZE(t6041_dcp_regs); i++) {
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, i);

if (!r || r->start != t6041_dcp_regs[i].start ||
resource_size(r) != t6041_dcp_regs[i].size ||
if (!r || r->start != regs[i].start ||
resource_size(r) != regs[i].size ||
of_property_match_string(dev->of_node, "reg-names",
t6041_dcp_regs[i].name) != i)
regs[i].name) != i)
return dev_err_probe(dev, -ENODEV,
"T6041 DCP: register window %d (%s) differs from the ADT\n",
i, t6041_dcp_regs[i].name);
i, regs[i].name);
}

if (!of_property_present(dev->of_node, "iommus") ||
Expand Down