File Index
Files and directories
Sign in
FILE

adp-mipi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/adp/adp-mipi.c C
  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(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
 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), 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
 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(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, 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
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(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
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(&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, adp->next_bridge, 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_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
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(&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");
~/ Read-only file preview.