Skip to content
Open
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
51 changes: 44 additions & 7 deletions arch/arm64/boot/dts/apple/t8132-j773g.dts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,50 @@
};
};

/*
* iBoot scans out through dispext0 to the HDMI port (dptx-phy and a
* DP-to-HDMI converter at i2c0 0x08, hot-plug on AOP GPIO 41): keep the
* power domains used for it on.
*/
&framebuffer0 {
power-domains = <&ps_dispext0_fe>, <&ps_dptx_phy>;
&soc {
dptx_phy: phy@39a700000 {
compatible = "apple,t8122-dptx-phy", "apple,dptx-phy";
reg = <0x3 0x9a700000 0x0 0x4000>,
<0x3 0x9a740000 0x0 0x10000>;
reg-names = "core", "dptx";
power-domains = <&ps_dptx_phy>;
#phy-cells = <0>;
status = "okay";
};
};

&dcp_mbox {
/* iBoot leaves DISP_CPU in auto mode; Linux must not switch it. */
/delete-property/ power-domains;
status = "okay";
};

&dcp_dart {
status = "okay";
};

&disp0_dart {
status = "okay";
};

&dcp {
/* iBoot leaves DISP_CPU in auto mode; Linux must not switch it. */
/delete-property/ power-domains;
apple,connector-type = "HDMI-A";
apple,dptx-phy = <4>;
phys = <&dptx_phy>;
phy-names = "dp-phy";
hdmi-pwren-gpios = <&smc_gpio 15 GPIO_ACTIVE_HIGH>;
dp2hdmi-pwren-gpios = <&smc_gpio 26 GPIO_ACTIVE_HIGH>;
status = "okay";
};

&disp0_piodma {
status = "okay";
};

&display {
status = "okay";
};

/*
Expand Down
71 changes: 57 additions & 14 deletions drivers/gpu/drm/apple/apple_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,8 @@ static int apple_probe_typec_ports(struct drm_device *drm,
return 0;
}

static bool apple_t8132_main_external_present(void);

static int apple_get_fb_resource(struct device *dev, const char *name,
struct resource *fb_r)
{
Expand All @@ -1364,7 +1366,9 @@ static int apple_get_fb_resource(struct device *dev, const char *name,
goto err;
}

if (!of_device_is_compatible(node, "framebuffer")) {
if (!of_device_is_compatible(node, "framebuffer") &&
!(apple_t8132_main_external_present() &&
of_device_is_compatible(node, "apple,asc-mem"))) {
dev_err(dev, "reserved-memory node '%s' is incompatible\n",
node->full_name);
goto err;
Expand Down Expand Up @@ -1425,6 +1429,16 @@ static const struct of_device_id apple_dcp_id_tbl[] = {
{},
};

static bool apple_t8132_main_external_present(void)
{
struct device_node *np = of_find_compatible_node(NULL, NULL,
"apple,t8132-dcp");
bool present = dcp_t8132_main_external(np) && of_device_is_available(np);

of_node_put(np);
return present;
}

/*
* A supported firmware-described external provider participates in component
* matching. A T6030 external display processor is a pipe of this DRM device
Expand All @@ -1433,7 +1447,7 @@ static const struct of_device_id apple_dcp_id_tbl[] = {
*/
static bool apple_dcp_usable(struct device_node *np)
{
return of_device_is_available(np) &&
return of_device_is_available(np) && !dcp_t8132_main_external(np) &&
(!of_device_is_compatible(np, "apple,t6030-dcpext") ||
dcp_t6030_ext_native(np)) &&
(!of_device_is_compatible(np, "apple,t8140-dcpext") ||
Expand Down Expand Up @@ -1488,6 +1502,15 @@ static void apple_dcp_start_fn(struct work_struct *work)
w->ret = dcp_start(w->dcp);
}

static bool apple_drm_has_output(struct drm_device *drm)
{
struct drm_crtc *crtc;

drm_for_each_crtc(crtc, drm)
return true;
return false;
}

static int apple_drm_init_dcp(struct device *dev)
{
struct apple_drm_private *apple = dev_get_drvdata(dev);
Expand Down Expand Up @@ -1545,8 +1568,17 @@ static int apple_drm_init_dcp(struct device *dev)
num_dcp++;
}

if (num_dcp < 1)
return -ENODEV;
if (num_dcp < 1) {
if (!apple_t8132_main_external_present())
return -ENODEV;
ret = t8132_ext_drm_init(&apple->drm);
if (ret)
dev_warn(dev, "T8132 main external output was not registered: %d\n",
ret);
else if (!apple_drm_has_output(&apple->drm))
ret = -ENODEV;
return ret;
}

/*
* Build the Type-C port connectors before starting any pipeline: a
Expand Down Expand Up @@ -1682,9 +1714,11 @@ static int apple_drm_init(struct device *dev)
apple->drm.mode_config.funcs = &apple_mode_config_funcs;
apple->drm.mode_config.helper_private = &apple_mode_config_helpers;

ret = component_bind_all(dev, apple);
if (ret)
return ret;
if (!apple_t8132_main_external_present()) {
ret = component_bind_all(dev, apple);
if (ret)
return ret;
}

ret = apple_drm_init_dcp(dev);
if (ret)
Expand Down Expand Up @@ -1802,7 +1836,8 @@ static int apple_drm_init(struct device *dev)
err_unbind:
t8132_ext_drm_fini(&apple->drm);
apple_drm_quiesce_connectors(&apple->drm);
component_unbind_all(dev, NULL);
if (!apple_t8132_main_external_present())
component_unbind_all(dev, NULL);
dcp_unlink(&apple->drm);
return ret;
}
Expand All @@ -1825,7 +1860,8 @@ static void apple_drm_uninit(struct device *dev)
drm_dev_unplug(&apple->drm);
drm_atomic_helper_shutdown(&apple->drm);

component_unbind_all(dev, NULL);
if (!apple_t8132_main_external_present())
component_unbind_all(dev, NULL);

dcp_unlink(&apple->drm);
dev_set_drvdata(dev, NULL);
Expand Down Expand Up @@ -1900,10 +1936,14 @@ static int apple_platform_probe(struct platform_device *pdev)

/* add DCP components, handle less than 1 as probe error */
num_dcp = add_dcp_components(mdev, &match);
if (num_dcp < 1)
return -ENODEV;

ret = component_master_add_with_match(mdev, &apple_drm_ops, match);
if (num_dcp < 1) {
if (!apple_t8132_main_external_present())
return -ENODEV;
ret = apple_drm_init(mdev);
} else {
ret = component_master_add_with_match(mdev, &apple_drm_ops,
match);
}
if (ret)
return ret;

Expand All @@ -1919,7 +1959,10 @@ static int apple_platform_probe(struct platform_device *pdev)
static void apple_platform_remove(struct platform_device *pdev)
{
pm_vt_switch_unregister(&pdev->dev);
component_master_del(&pdev->dev, &apple_drm_ops);
if (apple_t8132_main_external_present())
apple_drm_uninit(&pdev->dev);
else
component_master_del(&pdev->dev, &apple_drm_ops);
}

static const struct of_device_id of_match[] = {
Expand Down
15 changes: 15 additions & 0 deletions drivers/gpu/drm/apple/dcp-m4-profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ static const struct dcp_m4_profile dcp_m4_profile_j713 = {
.timing = dcp_m4_timing_j713, .timing_elements = 1,
};

/* The J773g uses the shared firmware ABI for an external output, not a panel. */
static const struct dcp_m4_profile dcp_m4_profile_j773g = {
.name = "J773g", .machine = "apple,j773g",
.panel_width = 2560, .panel_height = 1664, .notch_rows = 64,
.boot_stride = 10240,
.swap_size = 0x1bd8, .swap_surface = 0x588, .swap_dva = 0xe38,
.swap_nullable = 0x1bcb, .swap_tail = 0x1bd6, .swap_nits = 0x35e,
.swap_intent = 0x17c,
.disabled_color = 0xff000000,
.done_method = 590, .done_size = 0x730, .open_method = 457,
.brightness_max = 500, .brightness_default = 120,
.width_mm = 0, .height_mm = 0,
};

/* The J715's panel timing, as macOS reports it (TimingElements, ID 2). */
static const u32 dcp_m4_timing_j715[2][8] = {
{ 2880, 2960, 8, 32, 40, 7710975, 1, 0 },
Expand Down Expand Up @@ -106,6 +120,7 @@ static const struct dcp_m4_profile dcp_m4_profile_j604 = {

static const struct dcp_m4_profile *const dcp_m4_profiles[] = {
&dcp_m4_profile_j713,
&dcp_m4_profile_j773g,
&dcp_m4_profile_j715,
&dcp_m4_profile_j604,
};
Expand Down
78 changes: 73 additions & 5 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5456,6 +5456,13 @@ static int dcp_connector_type_from_dt(struct device_node *np)
return DRM_MODE_CONNECTOR_Unknown;
}

/* A T8132 main DCP can serve a fixed external output instead of a panel. */
bool dcp_t8132_main_external(struct device_node *np)
{
return np && of_device_is_compatible(np, "apple,t8132-dcp") &&
dcp_connector_type_from_dt(np) == DRM_MODE_CONNECTOR_HDMIA;
}

/*
* The fixed route's crossbar selection and Type-C mux are taken at probe and
* must be released with the device: a crossbar left selected blocks the next
Expand Down Expand Up @@ -6337,6 +6344,47 @@ static int dcp_desktop_probe_routes(struct apple_dcp *dcp)
return 0;
}

/*
* A fixed HDMI main DCP reaches its sink through a T8122-style DPTX PHY and
* two converter power GPIOs. Require every resource and leave it powered for
* the retained firmware session.
*/
static int dcp_m4_hdmi_prepare(struct apple_dcp *dcp)
{
struct device *dev = dcp->dev;
int ret;

dcp->hw.num_dptx_ports = 1;
dcp->phy = devm_phy_get(dev, "dp-phy");
if (IS_ERR(dcp->phy))
return dev_err_probe(dev, PTR_ERR(dcp->phy),
"HDMI DP PHY unavailable\n");
dcp->fixed_phy = dcp->phy;
ret = phy_init(dcp->phy);
if (ret)
return dev_err_probe(dev, ret, "HDMI DP PHY init failed\n");
ret = phy_power_on(dcp->phy);
if (ret) {
phy_exit(dcp->phy);
return dev_err_probe(dev, ret, "HDMI DP PHY power failed\n");
}
ret = devm_add_action_or_reset(dev, dcp_desktop_phy_off, dcp->phy);
if (ret)
return ret;

dcp->hdmi_pwren = devm_gpiod_get(dev, "hdmi-pwren", GPIOD_OUT_HIGH);
if (IS_ERR(dcp->hdmi_pwren))
return dev_err_probe(dev, PTR_ERR(dcp->hdmi_pwren),
"HDMI power GPIO unavailable\n");
dcp->dp2hdmi_pwren = devm_gpiod_get(dev, "dp2hdmi-pwren", GPIOD_OUT_HIGH);
if (IS_ERR(dcp->dp2hdmi_pwren))
return dev_err_probe(dev, PTR_ERR(dcp->dp2hdmi_pwren),
"DP2HDMI power GPIO unavailable\n");
msleep(100);
dev_info(dev, "fixed HDMI PHY and converter rails prepared\n");
return 0;
}

/*
* An adopted T8132 dcpext offers its Type-C routes. It is no component of
* the DRM device (its outputs are added by t8132_ext_drm.c): the routes
Expand Down Expand Up @@ -6581,8 +6629,18 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev)
return ret;

if (iomfb_is_m4(dcp)) {
dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP;
dcp->connector_type = DRM_MODE_CONNECTOR_eDP;
bool main_external = dcp_t8132_main_external(dev->of_node);

if (main_external) {
dcp->fixed_connector_type = DRM_MODE_CONNECTOR_HDMIA;
dcp->connector_type = DRM_MODE_CONNECTOR_HDMIA;
ret = dcp_m4_hdmi_prepare(dcp);
if (ret)
return ret;
} else {
dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP;
dcp->connector_type = DRM_MODE_CONNECTOR_eDP;
}
bitmap_zero(dcp->iomfb_surfaces, DCP_MAX_PLANES);
set_bit(0, dcp->iomfb_surfaces);
ret = iomfb_m4_init(dcp);
Expand All @@ -6592,11 +6650,20 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev)
* An external DCP is no component of the DRM device. Once
* adoption starts, its session outlives the device.
*/
if (of_device_is_compatible(dev->of_node, "apple,t8132-dcpext")) {
if (of_device_is_compatible(dev->of_node, "apple,t8132-dcpext") ||
main_external) {
ret = dcp_pin_live_session(dcp);
if (!ret)
ret = iomfb_m4_adopt_external(dcp);
if (!ret)
if (!ret && main_external) {
ret = dcp_desktop_dptx_connect(dcp, 0);
if (ret) {
dev_warn(dev, "initial HDMI DPTX connect failed: %d\n",
ret);
ret = 0;
}
}
if (!ret && !main_external)
dcp_t8132_ext_routes(dcp);
return ret;
}
Expand Down Expand Up @@ -7079,7 +7146,8 @@ static void dcp_platform_remove(struct platform_device *pdev)
if (dcp)
av_audiosrv_quiesce(dcp);
/* An adopted external DCP never joined the DRM device. */
if (of_device_is_compatible(pdev->dev.of_node, "apple,t8132-dcpext")) {
if (of_device_is_compatible(pdev->dev.of_node, "apple,t8132-dcpext") ||
dcp_t8132_main_external(pdev->dev.of_node)) {
/* Enabled with its Type-C routes, see dcp_t8132_ext_routes(). */
if (dcp)
disable_delayed_work_sync(&dcp->typec_reconnect_wq);
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/apple/dcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

bool dcp_h17p_external_supported(struct device_node *np);
bool dcp_h17p_external_ready(struct device_node *np);
bool dcp_t8132_main_external(struct device_node *np);

struct apple_atomic_state {
struct drm_atomic_commit base;
Expand Down
16 changes: 11 additions & 5 deletions drivers/gpu/drm/apple/iomfb_m4.c
Original file line number Diff line number Diff line change
Expand Up @@ -2635,9 +2635,11 @@ int iomfb_m4_init(struct apple_dcp *owner)
u32 ready = 0;
int ret;
/* An external DCP the boot loader handed over: same firmware, no panel. */
bool external = of_device_is_compatible(dev->of_node, "apple,t8132-dcpext");
bool dcpext = of_device_is_compatible(dev->of_node, "apple,t8132-dcpext");
bool main_external = dcp_t8132_main_external(dev->of_node);
bool external = dcpext || main_external;

if (external &&
if (dcpext &&
(of_property_read_u32(dev->of_node, "apple,t8132-dcpext-firmware-ready", &ready) ||
ready != 1))
return dev_err_probe(dev, -ENODEV, "external DCP not handed over\n");
Expand All @@ -2657,10 +2659,14 @@ int iomfb_m4_init(struct apple_dcp *owner)
if (ret)
return ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "coproc");
if (!res || resource_size(res) != SZ_16K ||
(external ? res->start != 0x286e00000ULL && res->start != 0x48ae00000ULL :
if (!res)
return dev_err_probe(dev, -ENODEV, "missing coproc resource\n");
if (resource_size(res) != SZ_16K ||
(dcpext ? res->start != 0x286e00000ULL && res->start != 0x48ae00000ULL :
res->start != 0x30ae00000ULL))
return -ENODEV;
return dev_err_probe(dev, -ENODEV,
"unqualified %s DCP coproc resource %pa-%pa\n",
dcpext ? "external" : "main", &res->start, &res->end);
/* Resolve the fallible supplier before creating a mailbox owner. */
ret = external ? 0 : apple_pmp_j713_display_prepare(dev);
if (ret)
Expand Down
Loading
Loading