WIP: dcp/dptx: Add support for dcp endpoint dptx

Supposedly required for HDMI out initilization on M2 (Pro)? Mac minis.
This commit is contained in:
Janne Grunau 2023-01-31 14:03:52 +01:00
parent cdb6d087b2
commit 32431d8ba1
12 changed files with 1183 additions and 28 deletions

View file

@ -73,6 +73,9 @@ LIBFDT_OBJECTS := $(patsubst %,libfdt/%, \
fdt_addresses.o fdt_empty_tree.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o \
fdt_wip.o fdt.o)
DCP_OBJECTS := $(patsubst %,dcp/%, \
dptxep.o parser.o)
OBJECTS := \
adt.o \
afk.o \
@ -128,6 +131,7 @@ OBJECTS := \
utils.o utils_asm.o \
vsprintf.o \
wdt.o \
$(DCP_OBJECTS) \
$(MINILZLIB_OBJECTS) $(TINF_OBJECTS) $(DLMALLOC_OBJECTS) $(LIBFDT_OBJECTS) $(RUST_LIBS)
FP_OBJECTS := \
@ -149,9 +153,9 @@ all: update_tag update_cfg build/$(TARGET) build/$(TARGET_RAW)
clean:
rm -rf build/*
format:
$(CLANG_FORMAT) -i src/*.c src/math/*.c src/*.h src/math/*.h sysinc/*.h
$(CLANG_FORMAT) -i src/*.c src/dcp/*.c src/math/*.c src/*.h src/dcp/*.h src/math/*.h sysinc/*.h
format-check:
$(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h sysinc/*.h
$(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h src/dcp/*.c src/dcp/*.h sysinc/*.h
rustfmt:
cd rust && cargo fmt
rustfmt-check:

188
src/afk.c
View file

@ -38,10 +38,6 @@ enum EPICCategory {
CAT_COMMAND = 0x30,
};
enum EPICMessage {
CODE_ANNOUNCE = 0x30,
};
struct afk_qe {
u32 magic;
u32 size;
@ -62,11 +58,12 @@ struct epic_sub_hdr {
u32 length;
u8 version;
u8 category;
u16 code;
u16 type;
u64 timestamp;
u16 seq;
u16 unk;
u32 unk2;
u8 unk;
u8 flags;
u32 inline_len;
} PACKED;
struct epic_announce {
@ -80,8 +77,11 @@ struct epic_cmd {
u64 txbuf;
u32 rxlen;
u32 txlen;
u16 pad;
} PACKED;
#define AFK_MAX_CHANNEL 16
struct afk_epic_ep {
int ep;
rtkit_dev_t *rtk;
@ -96,6 +96,10 @@ struct afk_epic_ep {
struct rtkit_buffer rxbuf;
bool started;
u16 seq;
const afk_epic_service_ops_t *ops;
afk_epic_service_t services[AFK_MAX_CHANNEL];
};
enum RBEP_MSG {
@ -345,8 +349,118 @@ static void afk_epic_rx_ack(afk_epic_ep_t *epic)
rb->hdr->rptr = rptr;
}
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
void *rxbuf, size_t *rxsize)
struct epic_std_service_ap_call {
u32 unk0;
u32 unk1;
u32 type;
u32 len;
u32 magic;
u8 _unk[48];
} PACKED;
static int afk_epic_handle_std_service(afk_epic_ep_t *epic, int channel, struct epic_hdr *hdr,
struct epic_sub_hdr *sub, void *payload, size_t payload_size)
{
afk_epic_service_t *service = &epic->services[channel];
if (sub->category == CAT_NOTIFY) {
struct epic_std_service_ap_call *call = payload;
size_t call_size;
void *reply;
int ret;
if (payload_size < sizeof(*call))
return -1;
call_size = call->len;
if (payload_size < sizeof(*call) + call_size)
return -1;
if (!service->ops->call)
return 0;
reply = calloc(payload_size, 1);
if (!reply)
return -1;
ret = service->ops->call(service, call->type, payload + sizeof(*call), call_size,
reply + sizeof(*call), call_size);
if (ret) {
free(reply);
return ret;
}
memcpy(reply, call, sizeof(*call));
size_t tx_size = sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr) + payload_size;
void *msg = calloc(tx_size, 1);
u32 sub_seq = sub->seq;
struct epic_hdr *hdr = msg;
struct epic_sub_hdr *sub = msg + sizeof(struct epic_hdr);
hdr->version = 2;
hdr->seq = epic->seq++;
sub->length = payload_size;
sub->version = 4;
sub->category = CAT_REPLY;
sub->type = CODE_STD_SERVICE;
sub->seq = sub_seq; // service->seq++;
sub->flags = 0x08;
sub->inline_len = payload_size;
memcpy(msg + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr), reply, payload_size);
afk_epic_tx(epic, channel, TYPE_NOTIFY_ACK, msg, tx_size);
free(reply);
free(msg);
return 0;
}
printf("AFK: channel %d received unhandled standard service message: %x\n", channel,
sub->category);
return -1;
}
int afk_epic_report(afk_epic_ep_t *epic, int channel, u32 type, u16 sub_seq, void *payload,
size_t payload_size)
{
struct epic_hdr *hdr;
struct epic_sub_hdr *sub;
size_t bfr_size = sizeof(*hdr) + sizeof(*sub) + payload_size;
void *bfr = calloc(bfr_size, 1);
if (!bfr)
return -1;
memset(bfr, 0, bfr_size);
hdr = bfr;
sub = bfr + sizeof(*hdr);
hdr->version = 2;
hdr->seq = epic->seq++;
sub->length = payload_size;
sub->version = 4;
sub->category = CAT_REPORT;
sub->type = type;
sub->seq = sub_seq;
memcpy(bfr + sizeof(*hdr) + sizeof(*sub), payload, payload_size);
int ret = afk_epic_tx(epic, channel, TYPE_NOTIFY, bfr, bfr_size);
free(bfr);
if (ret < 0) {
printf("EPIC: failed to transmit notify\n");
return ret;
}
return 0;
}
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 sub_seq, u16 code, void *txbuf,
size_t txsize, void *rxbuf, size_t *rxsize)
{
struct {
struct epic_hdr hdr;
@ -359,13 +473,15 @@ int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, si
memset(&msg, 0, sizeof(msg));
u32 tag = epic->seq;
msg.hdr.version = 2;
msg.hdr.seq = 0;
msg.hdr.seq = epic->seq++;
msg.sub.length = sizeof(msg.cmd);
msg.sub.version = 3;
msg.sub.version = 4;
msg.sub.category = CAT_COMMAND;
msg.sub.code = code;
msg.sub.seq = 0;
msg.sub.type = code;
msg.sub.seq = sub_seq;
msg.cmd.txbuf = epic->txbuf.dva;
msg.cmd.txlen = txsize;
msg.cmd.rxbuf = epic->rxbuf.dva;
@ -396,9 +512,16 @@ int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, si
struct epic_hdr *hdr = (void *)(rmsg + 1);
struct epic_sub_hdr *sub = (void *)(hdr + 1);
if (sub->category != CAT_REPLY || sub->code != code) {
if (sub->category == CAT_NOTIFY && sub->type == CODE_STD_SERVICE) {
void *payload = rmsg->data + sizeof(struct epic_hdr) + sizeof(struct epic_sub_hdr);
size_t payload_size =
rmsg->size - sizeof(struct epic_hdr) - sizeof(struct epic_sub_hdr);
afk_epic_rx_ack(epic);
afk_epic_handle_std_service(epic, channel, hdr, sub, payload, payload_size);
continue;
} else if (sub->category != CAT_REPLY || sub->type != code) {
printf("EPIC: got unexpected message %02x:%04x during command\n", sub->category,
sub->code);
sub->type);
afk_epic_rx_ack(epic);
continue;
}
@ -482,7 +605,8 @@ int afk_epic_shutdown(afk_epic_ep_t *epic)
return 0;
}
int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, size_t rxsize)
int afk_epic_start_channel(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
const char *name)
{
int channel = -1;
struct afk_qe *msg;
@ -503,9 +627,9 @@ int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, siz
struct epic_hdr *hdr = (void *)(msg + 1);
struct epic_sub_hdr *sub = (void *)(hdr + 1);
if (sub->category != CAT_REPORT || sub->code != CODE_ANNOUNCE) {
if (sub->category != CAT_REPORT || sub->type != CODE_ANNOUNCE) {
printf("EPIC: got unexpected message %02x:%04x during iface start\n", sub->category,
sub->code);
sub->type);
afk_epic_rx_ack(epic);
continue;
}
@ -518,10 +642,34 @@ int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, siz
continue;
}
size_t props_size = sub->length - offsetof(struct epic_announce, props);
afk_epic_service_t *service = &epic->services[msg->channel];
service->ops = ops;
service->epic = epic;
service->intf = intf;
service->channel = msg->channel;
if (ops && ops->init && !ops->init(service, announce->props, props_size)) {
printf("EPIC: ignoring channel %d: %s\n", msg->channel, announce->name);
afk_epic_rx_ack(epic);
memset(service, 0, sizeof(*service));
continue;
}
channel = msg->channel;
break;
}
afk_epic_rx_ack(epic);
return channel;
}
int afk_epic_start_interface(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
const char *name, size_t txsize, size_t rxsize)
{
int channel = afk_epic_start_channel(epic, ops, intf, name);
if (channel == -1) {
printf("EPIC: too many unexpected messages, giving up\n");
return -1;
@ -537,9 +685,7 @@ int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, siz
return -1;
}
printf("EPIC: started interface %d (%s)\n", msg->channel, announce->name);
afk_epic_rx_ack(epic);
printf("EPIC: started interface %d (%s)\n", channel, name);
return channel;
}

View file

@ -3,15 +3,71 @@
#ifndef DCP_AFK_H
#define DCP_AFK_H
#include <stddef.h>
#include "rtkit.h"
#define MAX_PENDING_CMDS 8
typedef struct afk_epic_ep afk_epic_ep_t;
typedef struct afk_epic_service_ops afk_epic_service_ops_t;
enum EPICMessage {
CODE_ANNOUNCE = 0x30,
CODE_STD_SERVICE = 0xc0,
};
struct epic_cmd_info {
u16 code;
void *rxbuf;
void *txbuf;
u64 rxbuf_dma;
u64 txbuf_dma;
size_t rxlen;
size_t txlen;
u32 retcode;
bool done;
bool free_on_ack;
};
typedef struct afk_epic_service {
const afk_epic_service_ops_t *ops;
afk_epic_ep_t *epic;
void *intf;
struct epic_cmd_info cmds[MAX_PENDING_CMDS];
u8 cmd_tag;
u32 channel;
bool enabled;
u16 seq;
void *cookie;
} afk_epic_service_t;
typedef struct afk_epic_service_ops {
const char name[32];
bool (*init)(afk_epic_service_t *service, u8 *props, size_t props_size);
int (*call)(afk_epic_service_t *service, u32 idx, const void *data, size_t data_size,
void *reply, size_t reply_size);
} afk_epic_service_ops_t;
afk_epic_ep_t *afk_epic_init(rtkit_dev_t *rtkit, int endpoint);
int afk_epic_shutdown(afk_epic_ep_t *epic);
int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t insize, size_t outsize);
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
void *rxbuf, size_t *rxsize);
int afk_epic_start_channel(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
const char *name);
int afk_epic_start_interface(afk_epic_ep_t *epic, const afk_epic_service_ops_t *ops, void *intf,
const char *name, size_t insize, size_t outsize);
int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 sub_seq, u16 code, void *txbuf,
size_t txsize, void *rxbuf, size_t *rxsize);
int afk_epic_report(afk_epic_ep_t *epic, int channel, u32 type, u16 sub_seq, void *payload,
size_t payload_size);
#endif

View file

@ -7,12 +7,33 @@
#include "dart.h"
#include "rtkit.h"
typedef struct dp_phy_configure_opts {
u32 link_rate;
u32 lanes;
bool set_rate;
bool set_lanes;
} dp_phy_configure_opts_t;
typedef struct dptx_phy dptx_phy_t;
typedef struct afk_epic_service afk_epic_service_t;
typedef struct dptx_port {
bool enabled;
u32 unit;
afk_epic_service_t *service;
dp_phy_configure_opts_t phy_opts;
dptx_phy_t *phy;
u32 link_rate, pending_link_rate;
} dptx_port_t;
typedef struct {
dart_dev_t *dart_dcp;
dart_dev_t *dart_disp;
iova_domain_t *iovad_dcp;
asc_dev_t *asc;
rtkit_dev_t *rtkit;
dptx_port_t *port;
} dcp_dev_t;
dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path);

546
src/dcp/dptxep.c Normal file
View file

@ -0,0 +1,546 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
#include <stdbool.h>
#include <string.h>
#include "dptxep.h"
#include "malloc.h"
#include "parser.h"
#include "../afk.h"
#include "../dcp.h"
#include "../types.h"
#include "../utils.h"
#define DCP_DPTX_ENDPOINT 0x2a
#define TXBUF_LEN 0x4000
#define RXBUF_LEN 0x4000
struct dcpdptx_connection_cmd {
u32 unk;
u32 target;
} __attribute__((packed));
struct dcpdptx_hotplug_cmd {
u8 _pad0[16];
u32 unk;
} __attribute__((packed));
struct dptxport_apcall_link_rate {
u32 retcode;
u8 _unk0[12];
u32 link_rate;
u8 _unk1[12];
} __attribute__((packed));
struct dptxport_apcall_get_support {
u32 retcode;
u8 _unk0[12];
u32 supported;
u8 _unk1[12];
} __attribute__((packed));
struct dptxport_apcall_max_drive_settings {
u32 retcode;
u8 _unk0[12];
u32 max_drive_settings[2];
u8 _unk1[8];
};
struct dptxport_apcall_set_tiled {
u32 retcode;
};
struct epic_service_call {
u8 _pad0[2];
u16 group;
u32 command;
u32 data_len;
#define EPIC_SERVICE_CALL_MAGIC 0x69706378
u32 magic;
u8 _pad1[48];
} __attribute__((packed));
typedef struct dcp_dptx_if {
afk_epic_ep_t *epic; // ensure an afk_epic_ep_t pointer can be used as dcp_dptx_if_t
dcp_dev_t *dcp;
int channel;
struct dptx_port port[2];
} dcp_dptx_if_t;
static int afk_service_call(afk_epic_service_t *service, u16 group, u32 command, const void *data,
size_t data_len, size_t data_pad, void *output, size_t output_len,
size_t output_pad)
{
struct epic_service_call *call;
void *bfr;
size_t bfr_len = max(data_len + data_pad, output_len + output_pad) + sizeof(*call);
int ret;
u32 retlen;
size_t rx_len = bfr_len;
bfr = calloc(bfr_len, 1);
if (!bfr)
return -1;
call = bfr;
call->group = group;
call->command = command;
call->data_len = data_len + data_pad;
call->magic = EPIC_SERVICE_CALL_MAGIC;
memcpy(bfr + sizeof(*call), data, data_len);
ret = afk_epic_command(service->epic, service->channel, service->seq++, CODE_STD_SERVICE, bfr,
bfr_len, bfr, &rx_len);
if (ret)
goto out;
if (call->magic != EPIC_SERVICE_CALL_MAGIC || call->group != group ||
call->command != command) {
ret = -1;
goto out;
}
retlen = call->data_len;
if (output_len < retlen)
retlen = output_len;
if (output && output_len) {
memset(output, 0, output_len);
memcpy(output, bfr + sizeof(*call), retlen);
}
out:
free(bfr);
return ret;
}
int dptxport_validate_connection(afk_epic_service_t *service, u8 core, u8 atc, u8 die)
{
dptx_port_t *dptx = service->cookie;
struct dcpdptx_connection_cmd cmd, resp;
int ret;
u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
FIELD_PREP(DCPDPTX_REMOTE_PORT_DFP, atc) |
FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | DCPDPTX_REMOTE_PORT_CONNECTED;
cmd.target = target;
cmd.unk = 0x100;
ret = afk_service_call(service, 0, 14, &cmd, sizeof(cmd), 40, &resp, sizeof(resp), 40);
if (ret)
return ret;
if (resp.target != target)
return -1;
if (resp.unk != 0x100)
return -1;
return 0;
}
int dptxport_connect(afk_epic_service_t *service, u8 core, u8 atc, u8 die)
{
dptx_port_t *dptx = service->cookie;
struct dcpdptx_connection_cmd cmd = {0}, resp = {0};
int ret;
u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
FIELD_PREP(DCPDPTX_REMOTE_PORT_DFP, atc) |
FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) | DCPDPTX_REMOTE_PORT_CONNECTED;
cmd.target = target;
// cmd.unk = 0x100;
ret = afk_service_call(service, 0, 13, &cmd, sizeof(cmd), 24, &resp, sizeof(resp), 24);
if (ret)
return ret;
if (resp.target != target)
return -1;
if (resp.unk != 0x100)
return -1;
return 0;
}
int dptxport_request_display(afk_epic_service_t *service)
{
return afk_service_call(service, 0, 8, NULL, 0, 16, NULL, 0, 16);
}
int dptxport_release_display(afk_epic_service_t *service)
{
return afk_service_call(service, 0, 9, NULL, 0, 16, NULL, 0, 16);
}
int dptxport_set_hpd(afk_epic_service_t *service, bool hpd)
{
struct dcpdptx_hotplug_cmd cmd, resp;
int ret;
memset(&cmd, 0, sizeof(cmd));
if (hpd)
cmd.unk = 1;
ret = afk_service_call(service, 8, 10, &cmd, sizeof(cmd), 12, &resp, sizeof(resp), 12);
if (ret)
return ret;
if (resp.unk != 1)
return -1;
return 0;
}
static int dptxport_call_get_max_drive_settings(afk_epic_service_t *service, void *reply_,
size_t reply_size)
{
struct dptxport_apcall_max_drive_settings *reply = reply_;
if (reply_size < sizeof(*reply))
return -1;
reply->retcode = 0;
reply->max_drive_settings[0] = 0x3;
reply->max_drive_settings[1] = 0x3;
return 0;
}
static int dptxport_call_get_max_link_rate(afk_epic_service_t *service, void *reply_,
size_t reply_size)
{
struct dptxport_apcall_link_rate *reply = reply_;
if (reply_size < sizeof(*reply))
return -1;
reply->retcode = 0;
reply->link_rate = LINK_RATE_HBR3;
return 0;
}
static int dptxport_call_get_link_rate(afk_epic_service_t *service, void *reply_, size_t reply_size)
{
dptx_port_t *dptx = service->cookie;
struct dptxport_apcall_link_rate *reply = reply_;
if (reply_size < sizeof(*reply))
return -1;
reply->retcode = 0;
reply->link_rate = dptx->link_rate;
return 0;
}
static int dptxport_call_will_change_link_config(afk_epic_service_t *service)
{
dptx_port_t *dptx = service->cookie;
dptx->phy_opts.set_lanes = 0;
dptx->phy_opts.set_rate = 0;
return 0;
}
static int dptxport_call_did_change_link_config(afk_epic_service_t *service)
{
// struct dptx_port *dptx = service->cookie;
// int ret = 0;
mdelay(1000);
// dispext0,0 -> atcph1,dpphy
// mux_control_select(dptx->mux, 0);
return 0;
}
static int dptxport_call_set_link_rate(afk_epic_service_t *service, const void *data,
size_t data_size, void *reply_, size_t reply_size)
{
dptx_port_t *dptx = service->cookie;
const struct dptxport_apcall_link_rate *request = data;
struct dptxport_apcall_link_rate *reply = reply_;
u32 link_rate, phy_link_rate;
bool phy_set_rate = false;
int ret;
if (reply_size < sizeof(*reply))
return -1;
if (data_size < sizeof(*request))
return -1;
link_rate = request->link_rate;
switch (link_rate) {
case LINK_RATE_RBR:
phy_link_rate = 1620;
phy_set_rate = true;
break;
case LINK_RATE_HBR:
phy_link_rate = 2700;
phy_set_rate = true;
break;
case LINK_RATE_HBR2:
phy_link_rate = 5400;
phy_set_rate = true;
break;
case LINK_RATE_HBR3:
phy_link_rate = 8100;
phy_set_rate = true;
break;
case 0:
phy_link_rate = 0;
phy_set_rate = true;
break;
default:
printf("DPTXPort: Unsupported link rate 0x%x requested\n", link_rate);
link_rate = 0;
phy_set_rate = false;
break;
}
if (phy_set_rate) {
dptx->phy_opts.link_rate = phy_link_rate;
dptx->phy_opts.set_rate = 1;
if (dptx->phy) {
ret = 0; // phy_configure(dptx->atcphy, &dptx->phy_ops);
if (ret)
return ret;
}
// if (dptx->phy_ops.dp.set_rate)
dptx->link_rate = dptx->pending_link_rate = link_rate;
}
// dptx->pending_link_rate = link_rate;
reply->retcode = 0;
reply->link_rate = link_rate;
return 0;
}
static int dptxport_call_get_supports_hpd(afk_epic_service_t *service, void *reply_,
size_t reply_size)
{
struct dptxport_apcall_get_support *reply = reply_;
if (reply_size < sizeof(*reply))
return -1;
reply->retcode = 0;
reply->supported = 0;
return 0;
}
static int dptxport_call_get_supports_downspread(afk_epic_service_t *service, void *reply_,
size_t reply_size)
{
struct dptxport_apcall_get_support *reply = reply_;
if (reply_size < sizeof(*reply))
return -1;
reply->retcode = 0;
reply->supported = 0;
return 0;
}
static int dptxport_call_set_tiled_display_hint(afk_epic_service_t *service, void *reply_,
size_t reply_size)
{
struct dptxport_apcall_set_tiled *reply = reply_;
if (reply_size < sizeof(*reply))
return -1;
reply->retcode = 1;
return 0;
}
static int dptxport_call(afk_epic_service_t *service, u32 idx, const void *data, size_t data_size,
void *reply, size_t reply_size)
{
switch (idx) {
case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG:
return dptxport_call_will_change_link_config(service);
case DPTX_APCALL_DID_CHANGE_LINK_CONFIG:
return dptxport_call_did_change_link_config(service);
case DPTX_APCALL_GET_MAX_LINK_RATE:
return dptxport_call_get_max_link_rate(service, reply, reply_size);
case DPTX_APCALL_GET_LINK_RATE:
return dptxport_call_get_link_rate(service, reply, reply_size);
case DPTX_APCALL_SET_LINK_RATE:
return dptxport_call_set_link_rate(service, data, data_size, reply, reply_size);
case DPTX_APCALL_GET_SUPPORTS_HPD:
return dptxport_call_get_supports_hpd(service, reply, reply_size);
case DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD:
return dptxport_call_get_supports_downspread(service, reply, reply_size);
case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS:
return dptxport_call_get_max_drive_settings(service, reply, reply_size);
case DPTX_APCALL_SET_TILED_DISPLAY_HINTS:
memcpy(reply, data, min(reply_size, data_size));
return dptxport_call_set_tiled_display_hint(service, reply, reply_size);
default:
/* just try to ACK and hope for the best... */
printf("DPTXPort: unhandled call %d\n", idx);
// fallthrough
/* we can silently ignore and just ACK these calls */
case DPTX_APCALL_ACTIVATE:
case DPTX_APCALL_DEACTIVATE:
case DPTX_APCALL_SET_DRIVE_SETTINGS:
case DPTX_APCALL_GET_DRIVE_SETTINGS:
case DPTX_APCALL_SET_ACTIVE_LANE_COUNT:
memcpy(reply, data, min(reply_size, data_size));
if (reply_size > 4)
memset(reply, 0, 4);
return 0;
}
}
static bool dptxport_init(afk_epic_service_t *service, u8 *props, size_t props_size)
{
s64 unit;
const char *name = NULL, *class = NULL;
struct dcp_parse_ctx ctx;
dcp_dptx_if_t *dptx = (dcp_dptx_if_t *)service->epic;
int ret;
dptx = service->intf;
ret = parse(props, props_size, &ctx);
if (ret) {
printf("DPTXPort: failed to parse init props: %d\n", ret);
return false;
}
ret = parse_epic_service_init(&ctx, &name, &class, &unit);
if (ret) {
printf("DPTXPort: failed to extract init props: %d\n", ret);
return false;
}
printf("DPTXPort: parsed: name:'%s' class:'%s' unit:'%ld'\n", name, class, unit);
if (strcmp(name, "dcpdptx-port-epic"))
goto free;
if (strcmp(class, "AppleDCPDPTXRemotePort"))
goto free;
free((void *)name);
free((void *)class);
switch (unit) {
case 0:
case 1:
if (dptx->port[unit].enabled) {
printf("DPTXPort: unit %ld already exists\n", unit);
return false;
}
dptx->port[unit].unit = unit;
dptx->port[unit].service = service;
dptx->port[unit].enabled = true;
service->cookie = (void *)&dptx->port[unit];
printf("%s:%d port %ld enabled service:%p\n", __func__, __LINE__, unit, service);
break;
default:
printf("DPTXPort: invalid unit %ld\n", unit);
return false;
}
return true;
free:
free((void *)name);
free((void *)class);
return false;
}
static const afk_epic_service_ops_t dcp_dptx_ops = {
.name = "AppleDCPDPTXRemotePort",
.init = dptxport_init,
.call = dptxport_call,
};
int dcp_dptx_connect(dcp_dptx_if_t *dptx, u32 port)
{
if (!dptx->port[port].service)
return -1;
// dptx->port[port].atcphy = phy;
// dptxport_validate_connection(dptx->port[port].service, 0, 5, 0);
dptxport_connect(dptx->port[port].service, 0, 5, 0);
dptxport_request_display(dptx->port[port].service);
return 0;
}
int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd)
{
if (!dptx->port[port].service)
return -1;
dptxport_set_hpd(dptx->port[port].service, hpd);
return 0;
}
int dcp_dptx_disconnect(dcp_dptx_if_t *dptx, u32 port)
{
dptxport_release_display(dptx->port[port].service);
dptxport_set_hpd(dptx->port[port].service, false);
return 0;
}
dcp_dptx_if_t *dcp_dptx_init(dcp_dev_t *dcp)
{
int channel;
dcp_dptx_if_t *dptx = malloc(sizeof(dcp_dptx_if_t));
if (!dptx)
return NULL;
dptx->dcp = dcp;
dptx->epic = afk_epic_init(dcp->rtkit, DCP_DPTX_ENDPOINT);
if (!dptx->epic) {
printf("dcp-dptx: failed to initialize EPIC\n");
goto err_free;
}
channel = afk_epic_start_interface(dptx->epic, &dcp_dptx_ops, dptx,
"dispext4294967295:dcpdptx-port-", TXBUF_LEN, RXBUF_LEN);
if (channel < 0) {
printf("dcp-dptx: failed to initialize DPTXRemotePort service\n");
goto err_shutdown;
}
channel =
afk_epic_start_channel(dptx->epic, &dcp_dptx_ops, dptx, "dispext4294967295:dcpdptx-port-");
if (channel < 0) {
printf("dcp-dptx: failed to initialize DPTXRemotePort service\n");
goto err_shutdown;
}
u8 report_1[8] = {0x62, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
afk_epic_report(dptx->epic, 1, 0x12, 0, report_1, sizeof(report_1));
u8 report_3[8] = {0x63, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};
afk_epic_report(dptx->epic, 3, 0x12, 0, report_3, sizeof(report_3));
return dptx;
err_shutdown:
afk_epic_shutdown(dptx->epic);
err_free:
free(dptx);
return NULL;
}
int dcp_dptx_shutdown(dcp_dptx_if_t *dptx)
{
afk_epic_shutdown(dptx->epic);
free(dptx);
return 0;
}

58
src/dcp/dptxep.h Normal file
View file

@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
#ifndef __APPLE_DCP_DPTXEP_H__
#define __APPLE_DCP_DPTXEP_H__
#include "../dcp.h"
#include "../types.h"
typedef struct dcp_dptx_if dcp_dptx_if_t;
enum dptx_apcall {
DPTX_APCALL_ACTIVATE = 0,
DPTX_APCALL_DEACTIVATE = 1,
DPTX_APCALL_GET_MAX_DRIVE_SETTINGS = 2,
DPTX_APCALL_SET_DRIVE_SETTINGS = 3,
DPTX_APCALL_GET_DRIVE_SETTINGS = 4,
DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG = 5,
DPTX_APCALL_DID_CHANGE_LINK_CONFIG = 6,
DPTX_APCALL_GET_MAX_LINK_RATE = 7,
DPTX_APCALL_GET_LINK_RATE = 8,
DPTX_APCALL_SET_LINK_RATE = 9,
DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 10,
DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 11,
DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 12,
DPTX_APCALL_GET_DOWN_SPREAD = 13,
DPTX_APCALL_SET_DOWN_SPREAD = 14,
DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 15,
DPTX_APCALL_SET_LANE_MAP = 16,
DPTX_APCALL_GET_SUPPORTS_HPD = 17,
DPTX_APCALL_FORCE_HOTPLUG_DETECT = 18,
DPTX_APCALL_INACTIVE_SINK_DETECTED = 19,
DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 20,
DPTX_APCALL_DEVICE_NOT_RESPONDING = 21,
DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 22,
DPTX_APCALL_DEVICE_NOT_STARTED = 23,
};
#define DCPDPTX_REMOTE_PORT_CORE GENMASK(3, 0)
#define DCPDPTX_REMOTE_PORT_DFP GENMASK(7, 4)
#define DCPDPTX_REMOTE_PORT_DIE GENMASK(11, 8)
#define DCPDPTX_REMOTE_PORT_CONNECTED BIT(15)
enum dptx_link_rate {
LINK_RATE_RBR = 0x06,
LINK_RATE_HBR = 0x0a,
LINK_RATE_HBR2 = 0x14,
LINK_RATE_HBR3 = 0x1e,
};
dcp_dptx_if_t *dcp_dptx_init(dcp_dev_t *dcp);
int dcp_dptx_shutdown(dcp_dptx_if_t *dptx);
int dcp_dptx_connect(dcp_dptx_if_t *dptx, u32 port);
int dcp_dptx_disconnect(dcp_dptx_if_t *dptx, u32 port);
int dcp_dptx_hpd(dcp_dptx_if_t *dptx, u32 port, bool hpd);
#endif

271
src/dcp/parser.c Normal file
View file

@ -0,0 +1,271 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
#include "malloc.h"
#include "parser.h"
#include "string.h"
#include "../utils.h"
#define DCP_PARSE_HEADER 0xd3
enum dcp_parse_type {
DCP_TYPE_DICTIONARY = 1,
DCP_TYPE_ARRAY = 2,
DCP_TYPE_INT64 = 4,
DCP_TYPE_STRING = 9,
DCP_TYPE_BLOB = 10,
DCP_TYPE_BOOL = 11
};
struct dcp_parse_tag {
unsigned int size : 24;
enum dcp_parse_type type : 5;
unsigned int padding : 2;
bool last : 1;
} __packed;
static void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count)
{
void *ptr = ctx->blob + ctx->pos;
if (ctx->pos + count > ctx->len)
return NULL;
ctx->pos += count;
return ptr;
}
static u32 *parse_u32(struct dcp_parse_ctx *ctx)
{
return parse_bytes(ctx, sizeof(u32));
}
static struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx)
{
struct dcp_parse_tag *tag;
/* Align to 32-bits */
ctx->pos = ALIGN_UP(ctx->pos, 4);
tag = parse_bytes(ctx, sizeof(struct dcp_parse_tag));
if (!tag)
return NULL;
if (tag->padding)
return NULL;
return tag;
}
static struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx, enum dcp_parse_type type)
{
struct dcp_parse_tag *tag = parse_tag(ctx);
if (!tag)
return NULL;
if (tag->type != type)
return NULL;
return tag;
}
static int skip(struct dcp_parse_ctx *handle)
{
struct dcp_parse_tag *tag = parse_tag(handle);
int ret = 0;
int i;
if (!tag)
return -1;
switch (tag->type) {
case DCP_TYPE_DICTIONARY:
for (i = 0; i < tag->size; ++i) {
ret |= skip(handle); /* key */
ret |= skip(handle); /* value */
}
return ret;
case DCP_TYPE_ARRAY:
for (i = 0; i < tag->size; ++i)
ret |= skip(handle);
return ret;
case DCP_TYPE_INT64:
handle->pos += sizeof(s64);
return 0;
case DCP_TYPE_STRING:
case DCP_TYPE_BLOB:
handle->pos += tag->size;
return 0;
case DCP_TYPE_BOOL:
return 0;
default:
return -1;
}
}
/* Caller must free the result */
static char *parse_string(struct dcp_parse_ctx *handle)
{
struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING);
const char *in;
char *out;
if (!tag)
return NULL;
in = parse_bytes(handle, tag->size);
if (!in)
return NULL;
out = malloc(tag->size + 1);
memcpy(out, in, tag->size);
out[tag->size] = '\0';
return out;
}
static int parse_int(struct dcp_parse_ctx *handle, s64 *value)
{
void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64);
s64 *in;
if (!tag)
return -1;
in = parse_bytes(handle, sizeof(s64));
if (!in)
return -1;
memcpy(value, in, sizeof(*value));
return 0;
}
static int parse_bool(struct dcp_parse_ctx *handle, bool *b)
{
struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL);
if (!tag)
return -1;
*b = !!tag->size;
return 0;
}
struct iterator {
struct dcp_parse_ctx *handle;
u32 idx, len;
};
static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it, bool dict)
{
struct dcp_parse_tag *tag;
enum dcp_parse_type type = dict ? DCP_TYPE_DICTIONARY : DCP_TYPE_ARRAY;
*it = (struct iterator){.handle = handle, .idx = 0};
tag = parse_tag_of_type(it->handle, type);
if (!tag)
return -1;
it->len = tag->size;
return 0;
}
#define dcp_parse_foreach_in_array(handle, it) \
for (iterator_begin(handle, &it, false); it.idx < it.len; ++it.idx)
#define dcp_parse_foreach_in_dict(handle, it) \
for (iterator_begin(handle, &it, true); it.idx < it.len; ++it.idx)
int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx)
{
u32 *header;
*ctx = (struct dcp_parse_ctx){
.blob = blob,
.len = size,
.pos = 0,
};
header = parse_u32(ctx);
if (!header)
return -1;
if (*header != DCP_PARSE_HEADER)
return -1;
return 0;
}
int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, const char **class,
s64 *unit)
{
int ret = 0;
struct iterator it;
bool parsed_unit = false;
bool parsed_name = false;
bool parsed_class = false;
*name = NULL;
*class = NULL;
dcp_parse_foreach_in_dict(handle, it)
{
char *key = parse_string(it.handle);
if (!key) {
ret = -1;
break;
}
if (!strcmp(key, "EPICName")) {
*name = parse_string(it.handle);
if (!*name)
ret = -1;
else
parsed_name = true;
} else if (!strcmp(key, "EPICProviderClass")) {
*class = parse_string(it.handle);
if (!*class)
ret = -1;
else
parsed_class = true;
} else if (!strcmp(key, "EPICUnit")) {
ret = parse_int(it.handle, unit);
if (!ret)
parsed_unit = true;
} else {
skip(it.handle);
}
free(key);
if (ret)
break;
}
if (!parsed_unit || !parsed_name || !parsed_class)
ret = -1;
if (ret) {
if (*name) {
free(*name);
*name = NULL;
}
if (*class) {
free(*class);
*class = NULL;
}
}
return ret;
}

19
src/dcp/parser.h Normal file
View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
#ifndef __APPLE_DCP_PARSER_H__
#define __APPLE_DCP_PARSER_H__
#include "../types.h"
struct dcp_parse_ctx {
void *blob;
u32 pos, len;
};
int parse(void *blob, size_t size, struct dcp_parse_ctx *ctx);
int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, const char **class,
s64 *unit);
#endif

View file

@ -98,7 +98,8 @@ dcp_iboot_if_t *dcp_ib_init(dcp_dev_t *dcp)
goto err_free;
}
iboot->channel = afk_epic_start_interface(iboot->epic, "disp0-service", TXBUF_LEN, RXBUF_LEN);
iboot->channel =
afk_epic_start_interface(iboot->epic, NULL, NULL, "disp0-service", TXBUF_LEN, RXBUF_LEN);
if (iboot->channel < 0) {
printf("dcp-iboot: failed to initialize disp0 service\n");
@ -130,7 +131,7 @@ static int dcp_ib_cmd(dcp_iboot_if_t *iboot, int op, size_t in_size)
iboot->txcmd.op = op;
iboot->txcmd.len = sizeof(struct txcmd) + in_size;
return afk_epic_command(iboot->epic, iboot->channel, 0xc0, iboot->txbuf,
return afk_epic_command(iboot->epic, iboot->channel, 0, 0xc0, iboot->txbuf,
sizeof(struct txcmd) + in_size, iboot->rxbuf, &rxsize);
}

View file

@ -11,6 +11,8 @@
#include "utils.h"
#include "xnuboot.h"
#include "dcp/dptxep.h"
#define DISPLAY_STATUS_DELAY 100
#define DISPLAY_STATUS_RETRIES 20
@ -24,6 +26,7 @@
static dcp_dev_t *dcp;
static dcp_iboot_if_t *iboot;
static dcp_dptx_if_t *dptx;
static u64 fb_dva;
static u64 fb_size;
bool display_is_external;
@ -199,6 +202,24 @@ int display_start_dcp(void)
return -1;
}
// Power on
int ret;
if ((ret = dcp_ib_set_power(iboot, true)) < 0) {
printf("display: failed to set power\n");
return ret;
}
dptx = dcp_dptx_init(dcp);
if (!dptx) {
printf("display: failed to initialize DCP iBoot interface\n");
dcp_ib_shutdown(iboot);
iboot = NULL;
dcp_shutdown(dcp, false);
return -1;
}
dcp_dptx_connect(dptx, 0);
return 0;
}
@ -491,6 +512,8 @@ int display_init(void)
void display_shutdown(dcp_shutdown_mode mode)
{
if (iboot) {
if (dptx)
dcp_dptx_shutdown(dptx);
dcp_ib_shutdown(iboot);
switch (mode) {
case DCP_QUIESCED:

View file

@ -345,6 +345,14 @@ static void rtkit_crashed(rtkit_dev_t *rtk)
}
}
bool rtkit_can_recv(rtkit_dev_t *rtk)
{
if (rtk->crashed)
return false;
return asc_can_recv(rtk->asc);
}
int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg)
{
struct asc_message asc_msg;

View file

@ -31,6 +31,8 @@ void rtkit_free(rtkit_dev_t *rtk);
bool rtkit_start_ep(rtkit_dev_t *rtk, u8 ep);
bool rtkit_boot(rtkit_dev_t *rtk);
bool rtkit_can_recv(rtkit_dev_t *rtk);
int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg);
bool rtkit_send(rtkit_dev_t *rtk, const struct rtkit_message *msg);