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
24struct 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
33static 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(val: hdr_val, addr: 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
60static 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), addr: 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), addr: 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
96static 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(addr: 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
130static 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: &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: &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
159static int adp_dsi_bind(struct device *dev, struct device *master, void *data)
160{
161 return 0;
162}
163
164static void adp_dsi_unbind(struct device *dev, struct device *master, void *data)
165{
166}
167
168static const struct component_ops adp_dsi_component_ops = {
169 .bind = adp_dsi_bind,
170 .unbind = adp_dsi_unbind,
171};
172
173static 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(dev: adp->dsi.dev, node: adp->dsi.dev->of_node, port: 1, endpoint: 0);
181 if (IS_ERR(ptr: next))
182 return PTR_ERR(ptr: next);
183
184 adp->next_bridge = next;
185
186 drm_bridge_add(bridge: &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(bridge: &adp->bridge);
192 return ret;
193 }
194
195 return 0;
196}
197
198static 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(bridge: &adp->bridge);
205 return 0;
206}
207
208static 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
214static 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, bridge: adp->next_bridge, previous: bridge, flags);
222}
223
224static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
225 .attach = adp_dsi_bridge_attach,
226};
227
228static int adp_mipi_probe(struct platform_device *pdev)
229{
230 struct adp_mipi_drv_private *adp;
231
232 adp = devm_kzalloc(dev: &pdev->dev, size: sizeof(*adp), GFP_KERNEL);
233 if (!adp)
234 return -ENOMEM;
235
236 adp->mipi = devm_platform_ioremap_resource(pdev, index: 0);
237 if (IS_ERR(ptr: adp->mipi)) {
238 dev_err(&pdev->dev, "failed to map mipi mmio");
239 return PTR_ERR(ptr: adp->mipi);
240 }
241
242 adp->dsi.dev = &pdev->dev;
243 adp->dsi.ops = &adp_dsi_host_ops;
244 adp->bridge.funcs = &adp_dsi_bridge_funcs;
245 adp->bridge.of_node = pdev->dev.of_node;
246 adp->bridge.type = DRM_MODE_CONNECTOR_DSI;
247 dev_set_drvdata(dev: &pdev->dev, data: adp);
248 return mipi_dsi_host_register(host: &adp->dsi);
249}
250
251static 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(host: &adp->dsi);
257}
258
259static const struct of_device_id adp_mipi_of_match[] = {
260 { .compatible = "apple,h7-display-pipe-mipi", },
261 { },
262};
263MODULE_DEVICE_TABLE(of, adp_mipi_of_match);
264
265static 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
274module_platform_driver(adp_mipi_platform_driver);
275
276MODULE_DESCRIPTION("Apple Display Pipe MIPI driver");
277MODULE_LICENSE("GPL");
278

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of linux/drivers/gpu/drm/adp/adp-mipi.c