Skip to content

Commit dfc07cc

Browse files
povikjannau
authored andcommitted
gpu: drm: apple: Set up client of AV endpoint
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
1 parent 115ae87 commit dfc07cc

7 files changed

Lines changed: 344 additions & 0 deletions

File tree

drivers/gpu/drm/apple/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CFLAGS_trace.o = -I$(src)
55
appledrm-y := apple_drv.o
66

77
apple_dcp-y := afk.o dcp.o dcp_backlight.o dptxep.o iomfb.o parser.o systemep.o
8+
apple_dcp-$(CONFIG_DRM_APPLE_AUDIO) += av.o
89
apple_dcp-y += connector.o
910
apple_dcp-y += ibootep.o
1011
apple_dcp-y += iomfb_v12_3.o

drivers/gpu/drm/apple/audio.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef __AUDIO_H__
2+
#define __AUDIO_H__
3+
4+
#include <linux/types.h>
5+
6+
struct device;
7+
struct device_node;
8+
struct dcp_sound_cookie;
9+
10+
typedef void (*dcp_audio_hotplug_callback)(struct device *dev, bool connected);
11+
12+
struct dcp_audio_pdata {
13+
struct device *dcp_dev;
14+
struct device_node *dpaudio_node;
15+
};
16+
17+
void dcp_audiosrv_set_hotplug_cb(struct device *dev, struct device *audio_dev,
18+
dcp_audio_hotplug_callback cb);
19+
int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie);
20+
int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie);
21+
int dcp_audiosrv_stoplink(struct device *dev);
22+
int dcp_audiosrv_unprepare(struct device *dev);
23+
int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize);
24+
int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize);
25+
26+
#endif /* __AUDIO_H__ */

drivers/gpu/drm/apple/av.c

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
/* Copyright 2023 Martin Povišer <povik+lin@cutebit.org> */
3+
4+
// #define DEBUG
5+
6+
#include <linux/debugfs.h>
7+
#include <linux/kconfig.h>
8+
#include <linux/rwsem.h>
9+
#include <linux/types.h>
10+
11+
#include "audio.h"
12+
#include "afk.h"
13+
#include "dcp.h"
14+
15+
struct audiosrv_data {
16+
struct device *audio_dev;
17+
dcp_audio_hotplug_callback hotplug_cb;
18+
bool plugged;
19+
struct mutex plug_lock;
20+
21+
struct apple_epic_service *srv;
22+
struct rw_semaphore srv_rwsem;
23+
};
24+
25+
static void av_interface_init(struct apple_epic_service *service, const char *name,
26+
const char *class, s64 unit)
27+
{
28+
}
29+
30+
static void av_audiosrv_init(struct apple_epic_service *service, const char *name,
31+
const char *class, s64 unit)
32+
{
33+
struct apple_dcp *dcp = service->ep->dcp;
34+
struct audiosrv_data *asrv = dcp->audiosrv;
35+
int err;
36+
37+
mutex_lock(&asrv->plug_lock);
38+
39+
down_write(&asrv->srv_rwsem);
40+
asrv->srv = service;
41+
up_write(&asrv->srv_rwsem);
42+
43+
/* TODO: this must be done elsewhere */
44+
err = afk_service_call(asrv->srv, 0, 6, NULL, 0, 32, NULL, 0, 32);
45+
if (err)
46+
dev_err(dcp->dev, "error opening audio service: %d\n", err);
47+
48+
asrv->plugged = true;
49+
if (asrv->hotplug_cb)
50+
asrv->hotplug_cb(asrv->audio_dev, true);
51+
52+
mutex_unlock(&asrv->plug_lock);
53+
}
54+
55+
static void av_audiosrv_teardown(struct apple_epic_service *service)
56+
{
57+
struct apple_dcp *dcp = service->ep->dcp;
58+
struct audiosrv_data *asrv = dcp->audiosrv;
59+
60+
mutex_lock(&asrv->plug_lock);
61+
62+
down_write(&asrv->srv_rwsem);
63+
asrv->srv = NULL;
64+
up_write(&asrv->srv_rwsem);
65+
66+
asrv->plugged = false;
67+
if (asrv->hotplug_cb)
68+
asrv->hotplug_cb(asrv->audio_dev, false);
69+
70+
mutex_unlock(&asrv->plug_lock);
71+
}
72+
73+
void dcp_audiosrv_set_hotplug_cb(struct device *dev, struct device *audio_dev,
74+
dcp_audio_hotplug_callback cb)
75+
{
76+
struct apple_dcp *dcp = dev_get_drvdata(dev);
77+
struct audiosrv_data *asrv = dcp->audiosrv;
78+
79+
mutex_lock(&asrv->plug_lock);
80+
asrv->audio_dev = audio_dev;
81+
asrv->hotplug_cb = cb;
82+
83+
if (cb)
84+
cb(audio_dev, asrv->plugged);
85+
mutex_unlock(&asrv->plug_lock);
86+
}
87+
EXPORT_SYMBOL_GPL(dcp_audiosrv_set_hotplug_cb);
88+
89+
int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie)
90+
{
91+
struct apple_dcp *dcp = dev_get_drvdata(dev);
92+
struct audiosrv_data *asrv = dcp->audiosrv;
93+
int ret;
94+
95+
down_write(&asrv->srv_rwsem);
96+
ret = afk_service_call(asrv->srv, 0, 8, cookie, sizeof(*cookie),
97+
64 - sizeof(*cookie), NULL, 0, 64);
98+
up_write(&asrv->srv_rwsem);
99+
100+
return ret;
101+
}
102+
EXPORT_SYMBOL_GPL(dcp_audiosrv_prepare);
103+
104+
int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie)
105+
{
106+
struct apple_dcp *dcp = dev_get_drvdata(dev);
107+
struct audiosrv_data *asrv = dcp->audiosrv;
108+
int ret;
109+
110+
down_write(&asrv->srv_rwsem);
111+
ret = afk_service_call(asrv->srv, 0, 9, cookie, sizeof(*cookie),
112+
64 - sizeof(*cookie), NULL, 0, 64);
113+
up_write(&asrv->srv_rwsem);
114+
115+
return ret;
116+
}
117+
EXPORT_SYMBOL_GPL(dcp_audiosrv_startlink);
118+
119+
int dcp_audiosrv_stoplink(struct device *dev)
120+
{
121+
struct apple_dcp *dcp = dev_get_drvdata(dev);
122+
struct audiosrv_data *asrv = dcp->audiosrv;
123+
int ret;
124+
125+
down_write(&asrv->srv_rwsem);
126+
ret = afk_service_call(asrv->srv, 0, 12, NULL, 0, 64, NULL, 0, 64);
127+
up_write(&asrv->srv_rwsem);
128+
129+
return ret;
130+
}
131+
EXPORT_SYMBOL_GPL(dcp_audiosrv_stoplink);
132+
133+
int dcp_audiosrv_unprepare(struct device *dev)
134+
{
135+
struct apple_dcp *dcp = dev_get_drvdata(dev);
136+
struct audiosrv_data *asrv = dcp->audiosrv;
137+
int ret;
138+
139+
down_write(&asrv->srv_rwsem);
140+
ret = afk_service_call(asrv->srv, 0, 13, NULL, 0, 64, NULL, 0, 64);
141+
up_write(&asrv->srv_rwsem);
142+
143+
return ret;
144+
}
145+
EXPORT_SYMBOL_GPL(dcp_audiosrv_unprepare);
146+
147+
static int
148+
dcp_audiosrv_osobject_call(struct apple_epic_service *service, u16 group,
149+
u32 command, void *output, size_t output_maxsize,
150+
size_t *output_size)
151+
{
152+
struct {
153+
__le64 max_size;
154+
u8 _pad1[24];
155+
__le64 used_size;
156+
u8 _pad2[8];
157+
} __attribute__((packed)) *hdr;
158+
static_assert(sizeof(*hdr) == 48);
159+
size_t bfr_len = output_maxsize + sizeof(*hdr);
160+
void *bfr;
161+
int ret;
162+
163+
bfr = kzalloc(bfr_len, GFP_KERNEL);
164+
if (!bfr)
165+
return -ENOMEM;
166+
167+
hdr = bfr;
168+
hdr->max_size = cpu_to_le64(output_maxsize);
169+
ret = afk_service_call(service, group, command, hdr, sizeof(*hdr), output_maxsize,
170+
bfr, sizeof(*hdr) + output_maxsize, 0);
171+
if (ret)
172+
return ret;
173+
174+
if (output)
175+
memcpy(output, bfr + sizeof(*hdr), output_maxsize);
176+
177+
if (output_size)
178+
*output_size = le64_to_cpu(hdr->used_size);
179+
180+
return 0;
181+
}
182+
183+
int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize)
184+
{
185+
struct apple_dcp *dcp = dev_get_drvdata(dev);
186+
struct audiosrv_data *asrv = dcp->audiosrv;
187+
size_t size;
188+
int ret;
189+
190+
down_write(&asrv->srv_rwsem);
191+
ret = dcp_audiosrv_osobject_call(asrv->srv, 1, 18, elements, maxsize, &size);
192+
up_write(&asrv->srv_rwsem);
193+
194+
if (ret)
195+
dev_err(dev, "audiosrv: error getting elements: %d\n", ret);
196+
else
197+
dev_dbg(dev, "audiosrv: got %zd bytes worth of elements\n", size);
198+
199+
return ret;
200+
}
201+
EXPORT_SYMBOL_GPL(dcp_audiosrv_get_elements);
202+
203+
int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize)
204+
{
205+
struct apple_dcp *dcp = dev_get_drvdata(dev);
206+
struct audiosrv_data *asrv = dcp->audiosrv;
207+
size_t size;
208+
int ret;
209+
210+
down_write(&asrv->srv_rwsem);
211+
ret = dcp_audiosrv_osobject_call(asrv->srv, 1, 20, attrs, maxsize, &size);
212+
up_write(&asrv->srv_rwsem);
213+
214+
if (ret)
215+
dev_err(dev, "audiosrv: error getting product attributes: %d\n", ret);
216+
else
217+
dev_dbg(dev, "audiosrv: got %zd bytes worth of product attributes\n", size);
218+
219+
return ret;
220+
}
221+
EXPORT_SYMBOL_GPL(dcp_audiosrv_get_product_attrs);
222+
223+
static int av_audiosrv_report(struct apple_epic_service *service, u32 idx,
224+
const void *data, size_t data_size)
225+
{
226+
dev_dbg(service->ep->dcp->dev, "got audio report %d size %zx\n", idx, data_size);
227+
#ifdef DEBUG
228+
print_hex_dump(KERN_DEBUG, "audio report: ", DUMP_PREFIX_NONE, 16, 1, data, data_size, true);
229+
#endif
230+
231+
return 0;
232+
}
233+
234+
static const struct apple_epic_service_ops avep_ops[] = {
235+
{
236+
.name = "DCPAVSimpleVideoInterface",
237+
.init = av_interface_init,
238+
},
239+
{
240+
.name = "DCPAVAudioInterface",
241+
.init = av_audiosrv_init,
242+
.report = av_audiosrv_report,
243+
.teardown = av_audiosrv_teardown,
244+
},
245+
{}
246+
};
247+
248+
int avep_init(struct apple_dcp *dcp)
249+
{
250+
struct dcp_audio_pdata *audio_pdata;
251+
struct platform_device *audio_pdev;
252+
struct audiosrv_data *audiosrv_data;
253+
struct device *dev = dcp->dev;
254+
255+
audiosrv_data = devm_kzalloc(dcp->dev, sizeof(*audiosrv_data), GFP_KERNEL);
256+
audio_pdata = devm_kzalloc(dcp->dev, sizeof(*audio_pdata), GFP_KERNEL);
257+
if (!audiosrv_data || !audio_pdata)
258+
return -ENOMEM;
259+
init_rwsem(&audiosrv_data->srv_rwsem);
260+
mutex_init(&audiosrv_data->plug_lock);
261+
dcp->audiosrv = audiosrv_data;
262+
263+
audio_pdata->dcp_dev = dcp->dev;
264+
/* TODO: free OF reference */
265+
audio_pdata->dpaudio_node = \
266+
of_parse_phandle(dev->of_node, "apple,audio-xmitter", 0);
267+
if (!audio_pdata->dpaudio_node ||
268+
!of_device_is_available(audio_pdata->dpaudio_node)) {
269+
dev_info(dev, "No audio support\n");
270+
return 0;
271+
}
272+
273+
audio_pdev = platform_device_register_data(dev, "dcp-hdmi-audio",
274+
PLATFORM_DEVID_AUTO,
275+
audio_pdata, sizeof(*audio_pdata));
276+
if (IS_ERR(audio_pdev))
277+
return dev_err_probe(dev, PTR_ERR(audio_pdev), "registering audio device\n");
278+
279+
dcp->avep = afk_init(dcp, AV_ENDPOINT, avep_ops);
280+
if (IS_ERR(dcp->avep))
281+
return PTR_ERR(dcp->avep);
282+
dcp->avep->debugfs_entry = dcp->ep_debugfs[AV_ENDPOINT - 0x20];
283+
return afk_start(dcp->avep);
284+
}

drivers/gpu/drm/apple/av.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef __AV_H__
2+
#define __AV_H__
3+
4+
#include "parser.h"
5+
6+
//int avep_audiosrv_startlink(struct apple_dcp *dcp, struct dcp_sound_cookie *cookie);
7+
//int avep_audiosrv_stoplink(struct apple_dcp *dcp);
8+
9+
#endif /* __AV_H__ */

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum {
3434
TEST_ENDPOINT = 0x21,
3535
DCP_EXPERT_ENDPOINT = 0x22,
3636
DISP0_ENDPOINT = 0x23,
37+
AV_ENDPOINT = 0x29,
3738
DPTX_ENDPOINT = 0x2a,
3839
HDCP_ENDPOINT = 0x2b,
3940
REMOTE_ALLOC_ENDPOINT = 0x2d,
@@ -89,6 +90,8 @@ struct dcp_brightness {
8990
bool update;
9091
};
9192

93+
struct audiosrv_data;
94+
9295
/** laptop/AiO integrated panel parameters from DT */
9396
struct dcp_panel {
9497
/// panel width in millimeter
@@ -223,6 +226,9 @@ struct apple_dcp {
223226

224227
struct apple_dcp_afkep *ibootep;
225228

229+
struct apple_dcp_afkep *avep;
230+
struct audiosrv_data *audiosrv;
231+
226232
struct apple_dcp_afkep *dptxep;
227233

228234
struct dptx_port dptxport[2];

0 commit comments

Comments
 (0)