Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/adp/adp-mipi.c
26494 views
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
struct drm_encoder *encoder,
216
enum drm_bridge_attach_flags flags)
217
{
218
struct adp_mipi_drv_private *adp =
219
container_of(bridge, struct adp_mipi_drv_private, bridge);
220
221
return drm_bridge_attach(encoder, adp->next_bridge, bridge, flags);
222
}
223
224
static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
225
.attach = adp_dsi_bridge_attach,
226
};
227
228
static int adp_mipi_probe(struct platform_device *pdev)
229
{
230
struct adp_mipi_drv_private *adp;
231
232
adp = devm_drm_bridge_alloc(&pdev->dev, struct adp_mipi_drv_private,
233
bridge, &adp_dsi_bridge_funcs);
234
if (IS_ERR(adp))
235
return PTR_ERR(adp);
236
237
adp->mipi = devm_platform_ioremap_resource(pdev, 0);
238
if (IS_ERR(adp->mipi)) {
239
dev_err(&pdev->dev, "failed to map mipi mmio");
240
return PTR_ERR(adp->mipi);
241
}
242
243
adp->dsi.dev = &pdev->dev;
244
adp->dsi.ops = &adp_dsi_host_ops;
245
adp->bridge.of_node = pdev->dev.of_node;
246
adp->bridge.type = DRM_MODE_CONNECTOR_DSI;
247
dev_set_drvdata(&pdev->dev, adp);
248
return mipi_dsi_host_register(&adp->dsi);
249
}
250
251
static void adp_mipi_remove(struct platform_device *pdev)
252
{
253
struct device *dev = &pdev->dev;
254
struct adp_mipi_drv_private *adp = dev_get_drvdata(dev);
255
256
mipi_dsi_host_unregister(&adp->dsi);
257
}
258
259
static const struct of_device_id adp_mipi_of_match[] = {
260
{ .compatible = "apple,h7-display-pipe-mipi", },
261
{ },
262
};
263
MODULE_DEVICE_TABLE(of, adp_mipi_of_match);
264
265
static struct platform_driver adp_mipi_platform_driver = {
266
.driver = {
267
.name = "adp-mipi",
268
.of_match_table = adp_mipi_of_match,
269
},
270
.probe = adp_mipi_probe,
271
.remove = adp_mipi_remove,
272
};
273
274
module_platform_driver(adp_mipi_platform_driver);
275
276
MODULE_DESCRIPTION("Apple Display Pipe MIPI driver");
277
MODULE_LICENSE("GPL");
278
279