Skip to content

Commit

Permalink
display: Support the HDMI output on M2* desktop systems
Browse files Browse the repository at this point in the history
On M2* desktop systems the HDMI output is not managed by dcp/dcp0. This
allows more flexibility, for example dcp on the M2 Mac mini can be
routed to the USB-C/ThunderBolt ports.
For m1n1 this adds quite ab bit of complexity. In addition to the DCP
iboot endpoint/protocol two additional endpoints have to be started.
Only the dptx-port endpoint has a meaningful AP <=> DCP communication.
In addition we start the system endpoint to optionally enable more
verbose syslog messages (if RTKIT_SYSLOG is defined).
In addition the dptx-phy or lpdptx-phy has to be programmed. Those two
phy look mostly identical. MacOS seems to use a static DP configuration
(4 lanes, HBR3*) regardless of the connected display.
The added SMC gpio support is used to power the MCDP29xx DP to HDMI
converter on.
Config  is unfortunately a mess since Apple can't name the device tree
nodes consistently. On M2 Ultra devices only the four dcpext*/dispext*
on each die are used. M2 Ultra support is untested.

* link rate might differ on t602x based devices for 8k60 support

Signed-off-by: Janne Grunau <[email protected]>
  • Loading branch information
jannau committed Sep 22, 2023
1 parent 075267e commit ae41be1
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 34 deletions.
152 changes: 141 additions & 11 deletions src/dcp.c
Original file line number Diff line number Diff line change
@@ -1,49 +1,159 @@
/* SPDX-License-Identifier: MIT */

#include "dcp.h"
#include "../config.h"

#include "adt.h"
#include "afk.h"
#include "dcp.h"
#include "firmware.h"
#include "malloc.h"
#include "pmgr.h"
#include "rtkit.h"
#include "smc.h"
#include "string.h"
#include "utils.h"

dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path)
#include "dcp/dptx_phy.h"

struct adt_function_smc_gpio {
u32 phandle;
char four_cc[4];
u32 gpio;
u32 unk;
};

static char dcp_pmgr_dev[16] = "DISP0_CPU0";
static u32 dcp_die;

static int dcp_hdmi_dptx_init(dcp_dev_t *dcp, const display_config_t *cfg)
{
int node = adt_path_offset(adt, cfg->dp2hdmi_gpio);
if (node < 0) {
printf("dcp: failed to find dp2hdmi-gpio node '%s'\n", cfg->dp2hdmi_gpio);
return -1;
}
struct adt_function_smc_gpio dp2hdmi_pwr, hdmi_pwr;

int err =
adt_getprop_copy(adt, node, "function-dp2hdmi_pwr_en", &dp2hdmi_pwr, sizeof(dp2hdmi_pwr));
if (err < 0)
printf("dcp: failed to get dp2hdmi_pwr_en gpio\n");
else
dcp->dp2hdmi_pwr_gpio = dp2hdmi_pwr.gpio;
err = adt_getprop_copy(adt, node, "function-hdmi_pwr_en", &hdmi_pwr, sizeof(hdmi_pwr));
if (err < 0)
printf("dcp: failed to get hdmi_pwr_en gpio\n");
else
dcp->hdmi_pwr_gpio = hdmi_pwr.gpio;

if (dcp->dp2hdmi_pwr_gpio && dcp->hdmi_pwr_gpio) {
smc_dev_t *smc = smc_init();
if (smc) {
smc_write_u32(smc, dcp->dp2hdmi_pwr_gpio, 0x800001);
smc_write_u32(smc, dcp->hdmi_pwr_gpio, 0x800001);
smc_shutdown(smc);
}
}

dcp->phy = dptx_phy_init(cfg->dptx_phy, cfg->dcp_index);
if (!dcp->phy) {
printf("dcp: failed to init (lp)dptx-phy '%s'\n", cfg->dptx_phy);
return -1;
}

dcp->dpav_ep = dcp_dpav_init(dcp);
if (!dcp->dpav_ep) {
printf("dcp: failed to initialize dpav endpoint\n");
dcp_system_shutdown(dcp->system_ep);
return -1;
}

dcp->dptx_ep = dcp_dptx_init(dcp);
if (!dcp->dptx_ep) {
printf("dcp: failed to initialize dptx-port endpoint\n");
dcp_dpav_shutdown(dcp->dpav_ep);
dcp_system_shutdown(dcp->system_ep);
return -1;
}

#ifdef RTKIT_SYSLOG
// start system endpoint when extended logging is requested
dcp->system_ep = dcp_system_init(dcp);
if (!dcp->system_ep) {
printf("dcp: failed to initialize system endpoint\n");
return -1;
}

dcp_system_set_property_u64(dcp->system_ep, "gAFKConfigLogMask", 0xffff);
#endif

return 0;
}

int dcp_connect_dptx(dcp_dev_t *dcp)
{
if (dcp->dptx_ep && dcp->phy) {
return dcp_dptx_connect(dcp->dptx_ep, dcp->phy, 0);
}

return 0;
}

int dcp_work(dcp_dev_t *dcp)
{
return afk_epic_work(dcp->afk, -1);
}

dcp_dev_t *dcp_init(const display_config_t *cfg)
{
u32 sid;

int node = adt_path_offset(adt, "/arm-io/dart-dcp/mapper-dcp");
if (cfg && cfg->dptx_phy[0]) {
if (os_firmware.version != V13_5) {
printf("dcp: dtpx-port is only supported with V13_5 OS firmware.\n");
return NULL;
}

strncpy(dcp_pmgr_dev, cfg->pmgr_dev, sizeof(dcp_pmgr_dev));
dcp_die = cfg->die;
pmgr_adt_power_enable(cfg->dcp);
pmgr_adt_power_enable(cfg->dptx_phy);
}

int dart_node = adt_path_offset(adt, cfg->dcp_dart);
int node = adt_first_child_offset(adt, dart_node);
if (node < 0) {
printf("dcp: mapper-dcp not found!\n");
printf("dcp: mapper-dcp* not found!\n");
return NULL;
}
if (ADT_GETPROP(adt, node, "reg", &sid) < 0) {
printf("dcp: failed to read dart stream ID!\n");
return NULL;
}

dcp_dev_t *dcp = malloc(sizeof(dcp_dev_t));
dcp_dev_t *dcp = calloc(1, sizeof(dcp_dev_t));
if (!dcp)
return NULL;

dcp->dart_dcp = dart_init_adt(dcp_dart_path, 0, sid, true);
dcp->dart_dcp = dart_init_adt(cfg->dcp_dart, 0, sid, true);
if (!dcp->dart_dcp) {
printf("dcp: failed to initialize DCP DART\n");
goto out_free;
}
u64 vm_base = dart_vm_base(dcp->dart_dcp);
dart_setup_pt_region(dcp->dart_dcp, dcp_dart_path, sid, vm_base);
dart_setup_pt_region(dcp->dart_dcp, cfg->dcp_dart, sid, vm_base);

dcp->dart_disp = dart_init_adt(disp_dart_path, 0, 0, true);
dcp->dart_disp = dart_init_adt(cfg->disp_dart, 0, 0, true);
if (!dcp->dart_disp) {
printf("dcp: failed to initialize DISP DART\n");
goto out_dart_dcp;
}
// set disp0's page tables at dart-dcp's vm-base
dart_setup_pt_region(dcp->dart_disp, disp_dart_path, 0, vm_base);
dart_setup_pt_region(dcp->dart_disp, cfg->disp_dart, 0, vm_base);

dcp->iovad_dcp = iovad_init(vm_base + 0x10000000, vm_base + 0x20000000);

dcp->asc = asc_init(dcp_path);
dcp->asc = asc_init(cfg->dcp);
if (!dcp->asc) {
printf("dcp: failed to initialize ASC\n");
goto out_iovad;
Expand All @@ -66,8 +176,18 @@ dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char
goto out_rtkit;
}

if (cfg && cfg->dptx_phy[0]) {
int ret = dcp_hdmi_dptx_init(dcp, cfg);
if (ret < 0)
goto out_afk;
}

return dcp;

out_afk:
afk_epic_shutdown(dcp->afk);
rtkit_sleep(dcp->rtkit);
pmgr_reset(dcp_die, dcp_pmgr_dev);
out_rtkit:
rtkit_quiesce(dcp->rtkit);
rtkit_free(dcp->rtkit);
Expand All @@ -83,10 +203,20 @@ dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char

int dcp_shutdown(dcp_dev_t *dcp, bool sleep)
{
if (dcp->dptx_ep) {
if (sleep) {
printf("DCP: dcp_shutdown(sleep=true) is broken with dptx-port, quiesce instead\n");
sleep = false;
}
}
dcp_dptx_shutdown(dcp->dptx_ep);
dcp_dpav_shutdown(dcp->dpav_ep);
dcp_system_shutdown(dcp->system_ep);
free(dcp->phy);
afk_epic_shutdown(dcp->afk);
if (sleep) {
rtkit_sleep(dcp->rtkit);
pmgr_reset(0, "DISP0_CPU0");
pmgr_reset(dcp_die, dcp_pmgr_dev);
} else {
rtkit_quiesce(dcp->rtkit);
}
Expand Down
26 changes: 25 additions & 1 deletion src/dcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,40 @@
#include "dart.h"
#include "rtkit.h"

#include "dcp/dpav_ep.h"
#include "dcp/dptx_port_ep.h"
#include "dcp/system_ep.h"

typedef struct {
const char dcp[24];
const char dcp_dart[24];
const char disp_dart[24];
const char dptx_phy[24];
const char dp2hdmi_gpio[24];
const char pmgr_dev[24];
u32 dcp_index;
u8 die;
} display_config_t;

typedef struct dcp_dev {
dart_dev_t *dart_dcp;
dart_dev_t *dart_disp;
iova_domain_t *iovad_dcp;
asc_dev_t *asc;
rtkit_dev_t *rtkit;
afk_epic_t *afk;
dcp_system_if_t *system_ep;
dcp_dpav_if_t *dpav_ep;
dcp_dptx_if_t *dptx_ep;
dptx_phy_t *phy;
u32 dp2hdmi_pwr_gpio;
u32 hdmi_pwr_gpio;
} dcp_dev_t;

dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path);
int dcp_connect_dptx(dcp_dev_t *dcp);
int dcp_work(dcp_dev_t *dcp);

dcp_dev_t *dcp_init(const display_config_t *config);

int dcp_shutdown(dcp_dev_t *dcp, bool sleep);

Expand Down
Loading

0 comments on commit ae41be1

Please sign in to comment.