Skip to content

Commit 8108ddc

Browse files
committed
drm: apple: Add dcpav-service-ep
Known uses EDID retrieval and raw I2C access. Signed-off-by: Janne Grunau <j@jannau.net>
1 parent 7ead47e commit 8108ddc

9 files changed

Lines changed: 296 additions & 1 deletion

File tree

drivers/gpu/drm/apple/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ apple_dcp-y += connector.o
1111
apple_dcp-y += ibootep.o
1212
apple_dcp-y += iomfb_v12_3.o
1313
apple_dcp-y += iomfb_v13_3.o
14+
apple_dcp-y += epic/dpavservep.o
15+
1416
apple_dcp-$(CONFIG_TRACING) += trace.o
1517

1618
obj-$(CONFIG_DRM_APPLE) += appledrm.o

drivers/gpu/drm/apple/connector.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Copyright (C) The Asahi Linux Contributors
44
*/
55

6+
#include "connector.h"
7+
68
#include "linux/err.h"
79
#include <linux/debugfs.h>
810
#include <linux/module.h>
@@ -12,7 +14,6 @@
1214

1315
#include <drm/drm_managed.h>
1416

15-
#include "connector.h"
1617
#include "dcp-internal.h"
1718

1819
enum dcp_chunk_type {

drivers/gpu/drm/apple/connector.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <drm/drm_atomic.h>
1010
#include "drm/drm_connector.h"
1111

12+
struct apple_connector;
13+
1214
#include "dcp-internal.h"
1315

1416
void dcp_hotplug(struct work_struct *work);

drivers/gpu/drm/apple/dcp-internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
#include "iomfb.h"
1818
#include "iomfb_v12_3.h"
1919
#include "iomfb_v13_3.h"
20+
#include "epic/dpavservep.h"
2021

2122
#define DCP_MAX_PLANES 2
2223

2324
struct apple_dcp;
2425
struct apple_dcp_afkep;
2526

27+
struct dcpav_service_epic;
28+
2629
enum dcp_firmware_version {
2730
DCP_FIRMWARE_UNKNOWN,
2831
DCP_FIRMWARE_V_12_3,
@@ -34,6 +37,7 @@ enum {
3437
TEST_ENDPOINT = 0x21,
3538
DCP_EXPERT_ENDPOINT = 0x22,
3639
DISP0_ENDPOINT = 0x23,
40+
DPAVSERV_ENDPOINT = 0x28,
3741
AV_ENDPOINT = 0x29,
3842
DPTX_ENDPOINT = 0x2a,
3943
HDCP_ENDPOINT = 0x2b,
@@ -228,6 +232,8 @@ struct apple_dcp {
228232
struct completion systemep_done;
229233

230234
struct apple_dcp_afkep *ibootep;
235+
struct apple_dcp_afkep *dcpavservep;
236+
struct dcpavserv dcpavserv;
231237

232238
struct apple_dcp_afkep *avep;
233239
struct audiosrv_data *audiosrv;

drivers/gpu/drm/apple/dcp.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ bool hdmi_audio;
5050
module_param(hdmi_audio, bool, 0644);
5151
MODULE_PARM_DESC(hdmi_audio, "Enable unstable HDMI audio support");
5252

53+
static bool unstable_edid;
54+
module_param(unstable_edid, bool, 0644);
55+
MODULE_PARM_DESC(unstable_edid, "Enable unstable EDID retrival support");
56+
5357
/* copied and simplified from drm_vblank.c */
5458
static void send_vblank_event(struct drm_device *dev,
5559
struct drm_pending_vblank_event *e,
@@ -219,6 +223,9 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message)
219223
case DISP0_ENDPOINT:
220224
afk_receive_message(dcp->ibootep, message);
221225
return;
226+
case DPAVSERV_ENDPOINT:
227+
afk_receive_message(dcp->dcpavservep, message);
228+
return;
222229
case DPTX_ENDPOINT:
223230
afk_receive_message(dcp->dptxep, message);
224231
return;
@@ -477,6 +484,13 @@ int dcp_start(struct platform_device *pdev)
477484
if (ret)
478485
dev_warn(dcp->dev, "Failed to start system endpoint: %d\n", ret);
479486

487+
if (unstable_edid && !dcp_has_panel(dcp)) {
488+
ret = dpavservep_init(dcp);
489+
if (ret)
490+
dev_warn(dcp->dev, "Failed to start DPAVSERV endpoint: %d",
491+
ret);
492+
}
493+
480494
if (dcp->phy && dcp->fw_compat >= DCP_FIRMWARE_V_13_5) {
481495
ret = ibootep_init(dcp);
482496
if (ret)
@@ -1067,6 +1081,11 @@ static void dcp_comp_unbind(struct device *dev, struct device *main, void *data)
10671081
dcp->systemep = NULL;
10681082
}
10691083

1084+
if (dcp->dcpavservep) {
1085+
afk_shutdown(dcp->dcpavservep);
1086+
dcp->dcpavservep = NULL;
1087+
}
1088+
10701089
if (dcp->shmem)
10711090
iomfb_shutdown(dcp);
10721091

drivers/gpu/drm/apple/dcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ void iomfb_recv_msg(struct apple_dcp *dcp, u64 message);
6363
int systemep_init(struct apple_dcp *dcp);
6464
int dptxep_init(struct apple_dcp *dcp);
6565
int ibootep_init(struct apple_dcp *dcp);
66+
int dpavservep_init(struct apple_dcp *dcp);
6667
int avep_init(struct apple_dcp *dcp);
6768

6869

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/* Copyright The Asahi Linux Contributors */
3+
4+
#include "dpavservep.h"
5+
6+
#include <drm/drm_edid.h>
7+
8+
#include <linux/completion.h>
9+
#include <linux/device.h>
10+
#include <linux/types.h>
11+
12+
#include "../afk.h"
13+
#include "../dcp.h"
14+
#include "../dcp-internal.h"
15+
#include "../trace.h"
16+
17+
static void dcpavserv_init(struct apple_epic_service *service, const char *name,
18+
const char *class, s64 unit)
19+
{
20+
struct apple_dcp *dcp = service->ep->dcp;
21+
trace_dcpavserv_init(dcp, unit);
22+
23+
if (unit == 0 && name && !strcmp(name, "dcpav-service-epic")) {
24+
if (dcp->dcpavserv.enabled) {
25+
dev_err(dcp->dev,
26+
"DCPAVSERV: unit %lld already exists\n", unit);
27+
return;
28+
}
29+
dcp->dcpavserv.service = service;
30+
dcp->dcpavserv.enabled = true;
31+
service->cookie = &dcp->dcpavserv;
32+
complete(&dcp->dcpavserv.enable_completion);
33+
}
34+
}
35+
36+
static void dcpavserv_teardown(struct apple_epic_service *service)
37+
{
38+
struct apple_dcp *dcp = service->ep->dcp;
39+
if (dcp->dcpavserv.enabled) {
40+
dcp->dcpavserv.enabled = false;
41+
dcp->dcpavserv.service = NULL;
42+
service->cookie = NULL;
43+
reinit_completion(&dcp->dcpavserv.enable_completion);
44+
}
45+
}
46+
47+
static void dcpdpserv_init(struct apple_epic_service *service, const char *name,
48+
const char *class, s64 unit)
49+
{
50+
}
51+
52+
static void dcpdpserv_teardown(struct apple_epic_service *service)
53+
{
54+
}
55+
56+
struct dcpavserv_status_report {
57+
u32 unk00[4];
58+
u8 flag0;
59+
u8 flag1;
60+
u8 flag2;
61+
u8 flag3;
62+
u32 unk14[3];
63+
u32 status;
64+
u32 unk24[3];
65+
} __packed;
66+
67+
struct dpavserv_copy_edid_cmd {
68+
__le64 max_size;
69+
u8 _pad1[24];
70+
__le64 used_size;
71+
u8 _pad2[8];
72+
} __packed;
73+
74+
#define EDID_LEADING_DATA_SIZE 8
75+
#define EDID_BLOCK_SIZE 128
76+
#define EDID_EXT_BLOCK_COUNT_OFFSET 0x7E
77+
#define EDID_MAX_SIZE SZ_32K
78+
#define EDID_BUF_SIZE (EDID_LEADING_DATA_SIZE + EDID_MAX_SIZE)
79+
80+
struct dpavserv_copy_edid_resp {
81+
__le64 max_size;
82+
u8 _pad1[24];
83+
__le64 used_size;
84+
u8 _pad2[8];
85+
u8 data[];
86+
} __packed;
87+
88+
static int parse_report(struct apple_epic_service *service, enum epic_subtype type,
89+
const void *data, size_t data_size)
90+
{
91+
#if defined(DEBUG)
92+
struct apple_dcp *dcp = service->ep->dcp;
93+
const struct epic_service_call *call;
94+
const void *payload;
95+
size_t payload_size;
96+
97+
dev_dbg(dcp->dev, "dcpavserv[ch:%u]: report type:%02x len:%zu\n",
98+
service->channel, type, data_size);
99+
100+
if (type != EPIC_SUBTYPE_STD_SERVICE)
101+
return 0;
102+
103+
if (data_size < sizeof(*call))
104+
return 0;
105+
106+
call = data;
107+
108+
if (le32_to_cpu(call->magic) != EPIC_SERVICE_CALL_MAGIC) {
109+
dev_warn(dcp->dev, "dcpavserv[ch:%u]: report magic 0x%08x != 0x%08x\n",
110+
service->channel, le32_to_cpu(call->magic), EPIC_SERVICE_CALL_MAGIC);
111+
return 0;
112+
}
113+
114+
payload_size = data_size - sizeof(*call);
115+
if (payload_size < le32_to_cpu(call->data_len)) {
116+
dev_warn(dcp->dev, "dcpavserv[ch:%u]: report payload size %zu call len %u\n",
117+
service->channel, payload_size, le32_to_cpu(call->data_len));
118+
return 0;
119+
}
120+
payload_size = le32_to_cpu(call->data_len);
121+
payload = data + sizeof(*call);
122+
123+
if (le16_to_cpu(call->group) == 2 && le16_to_cpu(call->command) == 0) {
124+
if (payload_size == sizeof(struct dcpavserv_status_report)) {
125+
const struct dcpavserv_status_report *stat = payload;
126+
dev_info(dcp->dev, "dcpavserv[ch:%u]: flags: 0x%02x,0x%02x,0x%02x,0x%02x status:%u\n",
127+
service->channel, stat->flag0, stat->flag1,
128+
stat->flag2, stat->flag3, stat->status);
129+
} else {
130+
dev_dbg(dcp->dev, "dcpavserv[ch:%u]: report payload size %zu\n", service->channel, payload_size);
131+
}
132+
} else {
133+
print_hex_dump(KERN_DEBUG, "dcpavserv report: ", DUMP_PREFIX_NONE,
134+
16, 1, payload, payload_size, true);
135+
}
136+
#endif
137+
138+
return 0;
139+
}
140+
141+
static int dcpavserv_report(struct apple_epic_service *service,
142+
enum epic_subtype type, const void *data,
143+
size_t data_size)
144+
{
145+
return parse_report(service, type, data, data_size);
146+
}
147+
148+
static int dcpdpserv_report(struct apple_epic_service *service,
149+
enum epic_subtype type, const void *data,
150+
size_t data_size)
151+
{
152+
return parse_report(service, type, data, data_size);
153+
}
154+
155+
const struct drm_edid *dcpavserv_copy_edid(struct apple_epic_service *service)
156+
{
157+
struct dpavserv_copy_edid_cmd cmd;
158+
struct dpavserv_copy_edid_resp *resp __free(kfree) = NULL;
159+
int num_blocks;
160+
u64 data_size;
161+
int ret;
162+
163+
memset(&cmd, 0, sizeof(cmd));
164+
cmd.max_size = cpu_to_le64(EDID_BUF_SIZE);
165+
resp = kzalloc(sizeof(*resp) + EDID_BUF_SIZE, GFP_KERNEL);
166+
if (!resp)
167+
return ERR_PTR(-ENOMEM);
168+
169+
ret = afk_service_call(service, 1, 7, &cmd, sizeof(cmd), EDID_BUF_SIZE, resp,
170+
sizeof(resp) + EDID_BUF_SIZE, 0);
171+
if (ret < 0)
172+
return ERR_PTR(ret);
173+
174+
if (le64_to_cpu(resp->max_size) != EDID_BUF_SIZE)
175+
return ERR_PTR(-EIO);
176+
177+
// print_hex_dump(KERN_DEBUG, "dpavserv EDID cmd: ", DUMP_PREFIX_NONE,
178+
// 16, 1, resp, 192, true);
179+
180+
data_size = le64_to_cpu(resp->used_size);
181+
if (data_size < EDID_LEADING_DATA_SIZE + EDID_BLOCK_SIZE)
182+
return ERR_PTR(-EIO);
183+
184+
num_blocks = resp->data[EDID_LEADING_DATA_SIZE + EDID_EXT_BLOCK_COUNT_OFFSET];
185+
if ((1 + num_blocks) * EDID_BLOCK_SIZE != data_size - EDID_LEADING_DATA_SIZE)
186+
return ERR_PTR(-EIO);
187+
188+
return drm_edid_alloc(resp->data + EDID_LEADING_DATA_SIZE,
189+
data_size - EDID_LEADING_DATA_SIZE);
190+
}
191+
192+
static const struct apple_epic_service_ops dpavservep_ops[] = {
193+
{
194+
.name = "dcpav-service-epic",
195+
.init = dcpavserv_init,
196+
.teardown = dcpavserv_teardown,
197+
.report = dcpavserv_report,
198+
},
199+
{
200+
.name = "dcpdp-service-epic",
201+
.init = dcpdpserv_init,
202+
.teardown = dcpdpserv_teardown,
203+
.report = dcpdpserv_report,
204+
},
205+
{},
206+
};
207+
208+
int dpavservep_init(struct apple_dcp *dcp)
209+
{
210+
int ret;
211+
212+
init_completion(&dcp->dcpavserv.enable_completion);
213+
214+
dcp->dcpavservep = afk_init(dcp, DPAVSERV_ENDPOINT, dpavservep_ops);
215+
if (IS_ERR(dcp->dcpavservep))
216+
return PTR_ERR(dcp->dcpavservep);
217+
218+
dcp->dcpavservep->match_epic_name = true;
219+
220+
ret = afk_start(dcp->dcpavservep);
221+
if (ret)
222+
return ret;
223+
224+
ret = wait_for_completion_timeout(&dcp->dcpavserv.enable_completion,
225+
msecs_to_jiffies(1000));
226+
if (ret >= 0)
227+
return 0;
228+
229+
return ret;
230+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/* Copyright The Asahi Linux Contributors */
3+
4+
#ifndef _DRM_APPLE_EPIC_DPAVSERV_H
5+
#define _DRM_APPLE_EPIC_DPAVSERV_H
6+
7+
#include <linux/completion.h>
8+
#include <linux/types.h>
9+
10+
struct drm_edid;
11+
struct apple_epic_service;
12+
13+
struct dcpavserv {
14+
bool enabled;
15+
struct completion enable_completion;
16+
u32 unit;
17+
struct apple_epic_service *service;
18+
};
19+
20+
const struct drm_edid *dcpavserv_copy_edid(struct apple_epic_service *service);
21+
22+
#endif /* _DRM_APPLE_EPIC_DPAVSERV_H */

drivers/gpu/drm/apple/trace.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,18 @@ DEFINE_EVENT(iomfb_parse_mode_template, iomfb_parse_mode_fail,
351351
TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score),
352352
TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score));
353353

354+
TRACE_EVENT(dcpavserv_init, TP_PROTO(struct apple_dcp *dcp, u64 unit),
355+
TP_ARGS(dcp, unit),
356+
357+
TP_STRUCT__entry(__string(devname, dev_name(dcp->dev))
358+
__field(u64, unit)),
359+
360+
TP_fast_assign(__assign_str(devname);
361+
__entry->unit = unit;),
362+
363+
TP_printk("%s: dcpav-service unit %lld initialized", __get_str(devname),
364+
__entry->unit));
365+
354366
TRACE_EVENT(dptxport_init, TP_PROTO(struct apple_dcp *dcp, u64 unit),
355367
TP_ARGS(dcp, unit),
356368

0 commit comments

Comments
 (0)