Skip to content

Commit 332122e

Browse files
drm: adp: Add Apple Display Pipe driver
This display controller is present on M-series chips and is used to drive the touchbar display. Co-developed-by: Janne Grunau <j@jannau.net> Signed-off-by: Janne Grunau <j@jannau.net> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Reviewed-by: Neal Gompa <neal@gompa.dev> Signed-off-by: Sasha Finkelstein <fnkl.kernel@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/20250224-adpdrm-v8-2-cccf96710f0f@gmail.com Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
1 parent 7a108b9 commit 332122e

File tree

6 files changed

+917
-0
lines changed

6 files changed

+917
-0
lines changed

drivers/gpu/drm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
439439

440440
source "drivers/gpu/drm/tidss/Kconfig"
441441

442+
source "drivers/gpu/drm/adp/Kconfig"
443+
442444
source "drivers/gpu/drm/xlnx/Kconfig"
443445

444446
source "drivers/gpu/drm/gud/Kconfig"

drivers/gpu/drm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ obj-y += mxsfb/
206206
obj-y += tiny/
207207
obj-$(CONFIG_DRM_PL111) += pl111/
208208
obj-$(CONFIG_DRM_TVE200) += tve200/
209+
obj-$(CONFIG_DRM_ADP) += adp/
209210
obj-$(CONFIG_DRM_XEN) += xen/
210211
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
211212
obj-$(CONFIG_DRM_LIMA) += lima/

drivers/gpu/drm/adp/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
config DRM_ADP
3+
tristate "DRM Support for pre-DCP Apple display controllers"
4+
depends on DRM && OF && ARM64
5+
depends on ARCH_APPLE || COMPILE_TEST
6+
select DRM_KMS_HELPER
7+
select DRM_BRIDGE_CONNECTOR
8+
select DRM_DISPLAY_HELPER
9+
select DRM_KMS_DMA_HELPER
10+
select DRM_GEM_DMA_HELPER
11+
select DRM_PANEL_BRIDGE
12+
select VIDEOMODE_HELPERS
13+
select DRM_MIPI_DSI
14+
help
15+
Chose this option if you have an Apple Arm laptop with a touchbar.
16+
17+
If M is selected, this module will be called adpdrm.

drivers/gpu/drm/adp/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
adpdrm-y := adp_drv.o
4+
adpdrm-mipi-y := adp-mipi.o
5+
obj-$(CONFIG_DRM_ADP) += adpdrm.o adpdrm-mipi.o

drivers/gpu/drm/adp/adp-mipi.c

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/component.h>
4+
#include <linux/iopoll.h>
5+
#include <linux/of.h>
6+
#include <linux/platform_device.h>
7+
8+
#include <drm/drm_bridge.h>
9+
#include <drm/drm_mipi_dsi.h>
10+
11+
#define DSI_GEN_HDR 0x6c
12+
#define DSI_GEN_PLD_DATA 0x70
13+
14+
#define DSI_CMD_PKT_STATUS 0x74
15+
16+
#define GEN_PLD_R_EMPTY BIT(4)
17+
#define GEN_PLD_W_FULL BIT(3)
18+
#define GEN_PLD_W_EMPTY BIT(2)
19+
#define GEN_CMD_FULL BIT(1)
20+
#define GEN_CMD_EMPTY BIT(0)
21+
#define GEN_RD_CMD_BUSY BIT(6)
22+
#define CMD_PKT_STATUS_TIMEOUT_US 20000
23+
24+
struct adp_mipi_drv_private {
25+
struct mipi_dsi_host dsi;
26+
struct drm_bridge bridge;
27+
struct drm_bridge *next_bridge;
28+
void __iomem *mipi;
29+
};
30+
31+
#define mipi_to_adp(x) container_of(x, struct adp_mipi_drv_private, dsi)
32+
33+
static int adp_dsi_gen_pkt_hdr_write(struct adp_mipi_drv_private *adp, u32 hdr_val)
34+
{
35+
int ret;
36+
u32 val, mask;
37+
38+
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
39+
val, !(val & GEN_CMD_FULL), 1000,
40+
CMD_PKT_STATUS_TIMEOUT_US);
41+
if (ret) {
42+
dev_err(adp->dsi.dev, "failed to get available command FIFO\n");
43+
return ret;
44+
}
45+
46+
writel(hdr_val, adp->mipi + DSI_GEN_HDR);
47+
48+
mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
49+
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
50+
val, (val & mask) == mask,
51+
1000, CMD_PKT_STATUS_TIMEOUT_US);
52+
if (ret) {
53+
dev_err(adp->dsi.dev, "failed to write command FIFO\n");
54+
return ret;
55+
}
56+
57+
return 0;
58+
}
59+
60+
static int adp_dsi_write(struct adp_mipi_drv_private *adp,
61+
const struct mipi_dsi_packet *packet)
62+
{
63+
const u8 *tx_buf = packet->payload;
64+
int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
65+
__le32 word;
66+
u32 val;
67+
68+
while (len) {
69+
if (len < pld_data_bytes) {
70+
word = 0;
71+
memcpy(&word, tx_buf, len);
72+
writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA);
73+
len = 0;
74+
} else {
75+
memcpy(&word, tx_buf, pld_data_bytes);
76+
writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA);
77+
tx_buf += pld_data_bytes;
78+
len -= pld_data_bytes;
79+
}
80+
81+
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
82+
val, !(val & GEN_PLD_W_FULL), 1000,
83+
CMD_PKT_STATUS_TIMEOUT_US);
84+
if (ret) {
85+
dev_err(adp->dsi.dev,
86+
"failed to get available write payload FIFO\n");
87+
return ret;
88+
}
89+
}
90+
91+
word = 0;
92+
memcpy(&word, packet->header, sizeof(packet->header));
93+
return adp_dsi_gen_pkt_hdr_write(adp, le32_to_cpu(word));
94+
}
95+
96+
static int adp_dsi_read(struct adp_mipi_drv_private *adp,
97+
const struct mipi_dsi_msg *msg)
98+
{
99+
int i, j, ret, len = msg->rx_len;
100+
u8 *buf = msg->rx_buf;
101+
u32 val;
102+
103+
/* Wait end of the read operation */
104+
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
105+
val, !(val & GEN_RD_CMD_BUSY),
106+
1000, CMD_PKT_STATUS_TIMEOUT_US);
107+
if (ret) {
108+
dev_err(adp->dsi.dev, "Timeout during read operation\n");
109+
return ret;
110+
}
111+
112+
for (i = 0; i < len; i += 4) {
113+
/* Read fifo must not be empty before all bytes are read */
114+
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
115+
val, !(val & GEN_PLD_R_EMPTY),
116+
1000, CMD_PKT_STATUS_TIMEOUT_US);
117+
if (ret) {
118+
dev_err(adp->dsi.dev, "Read payload FIFO is empty\n");
119+
return ret;
120+
}
121+
122+
val = readl(adp->mipi + DSI_GEN_PLD_DATA);
123+
for (j = 0; j < 4 && j + i < len; j++)
124+
buf[i + j] = val >> (8 * j);
125+
}
126+
127+
return ret;
128+
}
129+
130+
static ssize_t adp_dsi_host_transfer(struct mipi_dsi_host *host,
131+
const struct mipi_dsi_msg *msg)
132+
{
133+
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
134+
struct mipi_dsi_packet packet;
135+
int ret, nb_bytes;
136+
137+
ret = mipi_dsi_create_packet(&packet, msg);
138+
if (ret) {
139+
dev_err(adp->dsi.dev, "failed to create packet: %d\n", ret);
140+
return ret;
141+
}
142+
143+
ret = adp_dsi_write(adp, &packet);
144+
if (ret)
145+
return ret;
146+
147+
if (msg->rx_buf && msg->rx_len) {
148+
ret = adp_dsi_read(adp, msg);
149+
if (ret)
150+
return ret;
151+
nb_bytes = msg->rx_len;
152+
} else {
153+
nb_bytes = packet.size;
154+
}
155+
156+
return nb_bytes;
157+
}
158+
159+
static int adp_dsi_bind(struct device *dev, struct device *master, void *data)
160+
{
161+
return 0;
162+
}
163+
164+
static void adp_dsi_unbind(struct device *dev, struct device *master, void *data)
165+
{
166+
}
167+
168+
static const struct component_ops adp_dsi_component_ops = {
169+
.bind = adp_dsi_bind,
170+
.unbind = adp_dsi_unbind,
171+
};
172+
173+
static int adp_dsi_host_attach(struct mipi_dsi_host *host,
174+
struct mipi_dsi_device *dev)
175+
{
176+
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
177+
struct drm_bridge *next;
178+
int ret;
179+
180+
next = devm_drm_of_get_bridge(adp->dsi.dev, adp->dsi.dev->of_node, 1, 0);
181+
if (IS_ERR(next))
182+
return PTR_ERR(next);
183+
184+
adp->next_bridge = next;
185+
186+
drm_bridge_add(&adp->bridge);
187+
188+
ret = component_add(host->dev, &adp_dsi_component_ops);
189+
if (ret) {
190+
pr_err("failed to add dsi_host component: %d\n", ret);
191+
drm_bridge_remove(&adp->bridge);
192+
return ret;
193+
}
194+
195+
return 0;
196+
}
197+
198+
static int adp_dsi_host_detach(struct mipi_dsi_host *host,
199+
struct mipi_dsi_device *dev)
200+
{
201+
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
202+
203+
component_del(host->dev, &adp_dsi_component_ops);
204+
drm_bridge_remove(&adp->bridge);
205+
return 0;
206+
}
207+
208+
static const struct mipi_dsi_host_ops adp_dsi_host_ops = {
209+
.transfer = adp_dsi_host_transfer,
210+
.attach = adp_dsi_host_attach,
211+
.detach = adp_dsi_host_detach,
212+
};
213+
214+
static int adp_dsi_bridge_attach(struct drm_bridge *bridge,
215+
enum drm_bridge_attach_flags flags)
216+
{
217+
struct adp_mipi_drv_private *adp =
218+
container_of(bridge, struct adp_mipi_drv_private, bridge);
219+
220+
return drm_bridge_attach(bridge->encoder, adp->next_bridge, bridge, flags);
221+
}
222+
223+
static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
224+
.attach = adp_dsi_bridge_attach,
225+
};
226+
227+
static int adp_mipi_probe(struct platform_device *pdev)
228+
{
229+
struct adp_mipi_drv_private *adp;
230+
231+
adp = devm_kzalloc(&pdev->dev, sizeof(*adp), GFP_KERNEL);
232+
if (!adp)
233+
return -ENOMEM;
234+
235+
adp->mipi = devm_platform_ioremap_resource(pdev, 0);
236+
if (IS_ERR(adp->mipi)) {
237+
dev_err(&pdev->dev, "failed to map mipi mmio");
238+
return PTR_ERR(adp->mipi);
239+
}
240+
241+
adp->dsi.dev = &pdev->dev;
242+
adp->dsi.ops = &adp_dsi_host_ops;
243+
adp->bridge.funcs = &adp_dsi_bridge_funcs;
244+
adp->bridge.of_node = pdev->dev.of_node;
245+
adp->bridge.type = DRM_MODE_CONNECTOR_DSI;
246+
dev_set_drvdata(&pdev->dev, adp);
247+
return mipi_dsi_host_register(&adp->dsi);
248+
}
249+
250+
static void adp_mipi_remove(struct platform_device *pdev)
251+
{
252+
struct device *dev = &pdev->dev;
253+
struct adp_mipi_drv_private *adp = dev_get_drvdata(dev);
254+
255+
mipi_dsi_host_unregister(&adp->dsi);
256+
}
257+
258+
static const struct of_device_id adp_mipi_of_match[] = {
259+
{ .compatible = "apple,h7-display-pipe-mipi", },
260+
{ },
261+
};
262+
MODULE_DEVICE_TABLE(of, adp_mipi_of_match);
263+
264+
static struct platform_driver adp_mipi_platform_driver = {
265+
.driver = {
266+
.name = "adp-mipi",
267+
.of_match_table = adp_mipi_of_match,
268+
},
269+
.probe = adp_mipi_probe,
270+
.remove = adp_mipi_remove,
271+
};
272+
273+
module_platform_driver(adp_mipi_platform_driver);
274+
275+
MODULE_DESCRIPTION("Apple Display Pipe MIPI driver");
276+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)