xref: /linux/drivers/media/platform/rockchip/rkcif/rkcif-dev.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1f53fb31aSMichael Riesch // SPDX-License-Identifier: GPL-2.0
2f53fb31aSMichael Riesch /*
3f53fb31aSMichael Riesch  * Rockchip Camera Interface (CIF) Driver
4f53fb31aSMichael Riesch  *
5f53fb31aSMichael Riesch  * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
6f53fb31aSMichael Riesch  * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
7f53fb31aSMichael Riesch  * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
8f53fb31aSMichael Riesch  * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
9f53fb31aSMichael Riesch  * Copyright (C) 2025 Collabora, Ltd.
10f53fb31aSMichael Riesch  */
11f53fb31aSMichael Riesch 
12f53fb31aSMichael Riesch #include <linux/clk.h>
13f53fb31aSMichael Riesch #include <linux/delay.h>
14f53fb31aSMichael Riesch #include <linux/interrupt.h>
15f53fb31aSMichael Riesch #include <linux/mfd/syscon.h>
16f53fb31aSMichael Riesch #include <linux/module.h>
17f53fb31aSMichael Riesch #include <linux/of.h>
18f53fb31aSMichael Riesch #include <linux/of_graph.h>
19f53fb31aSMichael Riesch #include <linux/of_platform.h>
20f53fb31aSMichael Riesch #include <linux/pm_runtime.h>
21f53fb31aSMichael Riesch #include <linux/reset.h>
22f53fb31aSMichael Riesch 
23f53fb31aSMichael Riesch #include <media/v4l2-fwnode.h>
24f53fb31aSMichael Riesch #include <media/v4l2-mc.h>
25f53fb31aSMichael Riesch 
2675e28594SMichael Riesch #include "rkcif-capture-dvp.h"
27*1f2353f5SMichael Riesch #include "rkcif-capture-mipi.h"
28f53fb31aSMichael Riesch #include "rkcif-common.h"
29f53fb31aSMichael Riesch 
30f53fb31aSMichael Riesch static const char *const px30_vip_clks[] = {
31f53fb31aSMichael Riesch 	"aclk",
32f53fb31aSMichael Riesch 	"hclk",
33f53fb31aSMichael Riesch 	"pclk",
34f53fb31aSMichael Riesch };
35f53fb31aSMichael Riesch 
36f53fb31aSMichael Riesch static const struct rkcif_match_data px30_vip_match_data = {
37f53fb31aSMichael Riesch 	.clks = px30_vip_clks,
38f53fb31aSMichael Riesch 	.clks_num = ARRAY_SIZE(px30_vip_clks),
3975e28594SMichael Riesch 	.dvp = &rkcif_px30_vip_dvp_match_data,
40f53fb31aSMichael Riesch };
41f53fb31aSMichael Riesch 
42f53fb31aSMichael Riesch static const char *const rk3568_vicap_clks[] = {
43f53fb31aSMichael Riesch 	"aclk",
44f53fb31aSMichael Riesch 	"hclk",
45f53fb31aSMichael Riesch 	"dclk",
46f53fb31aSMichael Riesch 	"iclk",
47f53fb31aSMichael Riesch };
48f53fb31aSMichael Riesch 
49f53fb31aSMichael Riesch static const struct rkcif_match_data rk3568_vicap_match_data = {
50f53fb31aSMichael Riesch 	.clks = rk3568_vicap_clks,
51f53fb31aSMichael Riesch 	.clks_num = ARRAY_SIZE(rk3568_vicap_clks),
52c348d671SMichael Riesch 	.dvp = &rkcif_rk3568_vicap_dvp_match_data,
53*1f2353f5SMichael Riesch 	.mipi = &rkcif_rk3568_vicap_mipi_match_data,
54f53fb31aSMichael Riesch };
55f53fb31aSMichael Riesch 
56f53fb31aSMichael Riesch static const struct of_device_id rkcif_plat_of_match[] = {
57f53fb31aSMichael Riesch 	{
58f53fb31aSMichael Riesch 		.compatible = "rockchip,px30-vip",
59f53fb31aSMichael Riesch 		.data = &px30_vip_match_data,
60f53fb31aSMichael Riesch 	},
61f53fb31aSMichael Riesch 	{
62f53fb31aSMichael Riesch 		.compatible = "rockchip,rk3568-vicap",
63f53fb31aSMichael Riesch 		.data = &rk3568_vicap_match_data,
64f53fb31aSMichael Riesch 	},
65f53fb31aSMichael Riesch 	{}
66f53fb31aSMichael Riesch };
67f53fb31aSMichael Riesch MODULE_DEVICE_TABLE(of, rkcif_plat_of_match);
68f53fb31aSMichael Riesch 
69f53fb31aSMichael Riesch static int rkcif_register(struct rkcif_device *rkcif)
70f53fb31aSMichael Riesch {
7175e28594SMichael Riesch 	int ret;
7275e28594SMichael Riesch 
7375e28594SMichael Riesch 	ret = rkcif_dvp_register(rkcif);
7475e28594SMichael Riesch 	if (ret && ret != -ENODEV)
7575e28594SMichael Riesch 		goto err;
7675e28594SMichael Riesch 
77*1f2353f5SMichael Riesch 	ret = rkcif_mipi_register(rkcif);
78*1f2353f5SMichael Riesch 	if (ret && ret != -ENODEV)
79*1f2353f5SMichael Riesch 		goto err_dvp_unregister;
80*1f2353f5SMichael Riesch 
81f53fb31aSMichael Riesch 	return 0;
8275e28594SMichael Riesch 
83*1f2353f5SMichael Riesch err_dvp_unregister:
84*1f2353f5SMichael Riesch 	rkcif_dvp_unregister(rkcif);
8575e28594SMichael Riesch err:
8675e28594SMichael Riesch 	return ret;
87f53fb31aSMichael Riesch }
88f53fb31aSMichael Riesch 
89f53fb31aSMichael Riesch static void rkcif_unregister(struct rkcif_device *rkcif)
90f53fb31aSMichael Riesch {
91*1f2353f5SMichael Riesch 	rkcif_mipi_unregister(rkcif);
9275e28594SMichael Riesch 	rkcif_dvp_unregister(rkcif);
93f53fb31aSMichael Riesch }
94f53fb31aSMichael Riesch 
95f53fb31aSMichael Riesch static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier,
96f53fb31aSMichael Riesch 				struct v4l2_subdev *sd,
97f53fb31aSMichael Riesch 				struct v4l2_async_connection *asd)
98f53fb31aSMichael Riesch {
9985411d17SMichael Riesch 	struct rkcif_device *rkcif =
10085411d17SMichael Riesch 		container_of(notifier, struct rkcif_device, notifier);
101f53fb31aSMichael Riesch 	struct rkcif_remote *remote =
102f53fb31aSMichael Riesch 		container_of(asd, struct rkcif_remote, async_conn);
10385411d17SMichael Riesch 	struct media_pad *sink_pad =
10485411d17SMichael Riesch 		&remote->interface->pads[RKCIF_IF_PAD_SINK];
10585411d17SMichael Riesch 	int ret;
10685411d17SMichael Riesch 
10785411d17SMichael Riesch 	ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
10885411d17SMichael Riesch 					      MEDIA_LNK_FL_ENABLED);
10985411d17SMichael Riesch 	if (ret) {
11085411d17SMichael Riesch 		dev_err(rkcif->dev, "failed to link source pad of %s\n",
11185411d17SMichael Riesch 			sd->name);
11285411d17SMichael Riesch 		return ret;
11385411d17SMichael Riesch 	}
114f53fb31aSMichael Riesch 
115f53fb31aSMichael Riesch 	remote->sd = sd;
116f53fb31aSMichael Riesch 
117f53fb31aSMichael Riesch 	return 0;
118f53fb31aSMichael Riesch }
119f53fb31aSMichael Riesch 
120f53fb31aSMichael Riesch static int rkcif_notifier_complete(struct v4l2_async_notifier *notifier)
121f53fb31aSMichael Riesch {
122f53fb31aSMichael Riesch 	struct rkcif_device *rkcif =
123f53fb31aSMichael Riesch 		container_of(notifier, struct rkcif_device, notifier);
124f53fb31aSMichael Riesch 
125f53fb31aSMichael Riesch 	return v4l2_device_register_subdev_nodes(&rkcif->v4l2_dev);
126f53fb31aSMichael Riesch }
127f53fb31aSMichael Riesch 
128f53fb31aSMichael Riesch static const struct v4l2_async_notifier_operations rkcif_notifier_ops = {
129f53fb31aSMichael Riesch 	.bound = rkcif_notifier_bound,
130f53fb31aSMichael Riesch 	.complete = rkcif_notifier_complete,
131f53fb31aSMichael Riesch };
132f53fb31aSMichael Riesch 
133f53fb31aSMichael Riesch static irqreturn_t rkcif_isr(int irq, void *ctx)
134f53fb31aSMichael Riesch {
135f53fb31aSMichael Riesch 	irqreturn_t ret = IRQ_NONE;
136f53fb31aSMichael Riesch 
13775e28594SMichael Riesch 	if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED)
13875e28594SMichael Riesch 		ret = IRQ_HANDLED;
13975e28594SMichael Riesch 
140*1f2353f5SMichael Riesch 	if (rkcif_mipi_isr(irq, ctx) == IRQ_HANDLED)
141*1f2353f5SMichael Riesch 		ret = IRQ_HANDLED;
142*1f2353f5SMichael Riesch 
143f53fb31aSMichael Riesch 	return ret;
144f53fb31aSMichael Riesch }
145f53fb31aSMichael Riesch 
146f53fb31aSMichael Riesch static int rkcif_probe(struct platform_device *pdev)
147f53fb31aSMichael Riesch {
148f53fb31aSMichael Riesch 	struct device *dev = &pdev->dev;
149f53fb31aSMichael Riesch 	struct rkcif_device *rkcif;
150f53fb31aSMichael Riesch 	int ret, irq;
151f53fb31aSMichael Riesch 
152f53fb31aSMichael Riesch 	rkcif = devm_kzalloc(dev, sizeof(*rkcif), GFP_KERNEL);
153f53fb31aSMichael Riesch 	if (!rkcif)
154f53fb31aSMichael Riesch 		return -ENOMEM;
155f53fb31aSMichael Riesch 
156f53fb31aSMichael Riesch 	rkcif->match_data = of_device_get_match_data(dev);
157f53fb31aSMichael Riesch 	if (!rkcif->match_data)
158f53fb31aSMichael Riesch 		return -ENODEV;
159f53fb31aSMichael Riesch 
160f53fb31aSMichael Riesch 	dev_set_drvdata(dev, rkcif);
161f53fb31aSMichael Riesch 	rkcif->dev = dev;
162f53fb31aSMichael Riesch 
163f53fb31aSMichael Riesch 	rkcif->base_addr = devm_platform_ioremap_resource(pdev, 0);
164f53fb31aSMichael Riesch 	if (IS_ERR(rkcif->base_addr))
165f53fb31aSMichael Riesch 		return PTR_ERR(rkcif->base_addr);
166f53fb31aSMichael Riesch 
167f53fb31aSMichael Riesch 	irq = platform_get_irq(pdev, 0);
168f53fb31aSMichael Riesch 	if (irq < 0)
169f53fb31aSMichael Riesch 		return irq;
170f53fb31aSMichael Riesch 
171f53fb31aSMichael Riesch 	ret = devm_request_irq(dev, irq, rkcif_isr, IRQF_SHARED,
172f53fb31aSMichael Riesch 			       dev_driver_string(dev), dev);
173f53fb31aSMichael Riesch 	if (ret)
174f53fb31aSMichael Riesch 		return dev_err_probe(dev, ret, "failed to request irq\n");
175f53fb31aSMichael Riesch 
176f53fb31aSMichael Riesch 	if (rkcif->match_data->clks_num > RKCIF_CLK_MAX)
177f53fb31aSMichael Riesch 		return dev_err_probe(dev, -EINVAL, "invalid number of clocks\n");
178f53fb31aSMichael Riesch 
179f53fb31aSMichael Riesch 	rkcif->clks_num = rkcif->match_data->clks_num;
180f53fb31aSMichael Riesch 	for (unsigned int i = 0; i < rkcif->clks_num; i++)
181f53fb31aSMichael Riesch 		rkcif->clks[i].id = rkcif->match_data->clks[i];
182f53fb31aSMichael Riesch 	ret = devm_clk_bulk_get(dev, rkcif->clks_num, rkcif->clks);
183f53fb31aSMichael Riesch 	if (ret)
184f53fb31aSMichael Riesch 		return dev_err_probe(dev, ret, "failed to get clocks\n");
185f53fb31aSMichael Riesch 
186f53fb31aSMichael Riesch 	rkcif->reset = devm_reset_control_array_get_exclusive(dev);
187f53fb31aSMichael Riesch 	if (IS_ERR(rkcif->reset))
188f53fb31aSMichael Riesch 		return PTR_ERR(rkcif->reset);
189f53fb31aSMichael Riesch 
190f53fb31aSMichael Riesch 	rkcif->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
191f53fb31aSMichael Riesch 						     "rockchip,grf");
192f53fb31aSMichael Riesch 	if (IS_ERR(rkcif->grf))
193f53fb31aSMichael Riesch 		rkcif->grf = NULL;
194f53fb31aSMichael Riesch 
195f53fb31aSMichael Riesch 	pm_runtime_enable(&pdev->dev);
196f53fb31aSMichael Riesch 
197f53fb31aSMichael Riesch 	rkcif->media_dev.dev = dev;
198f53fb31aSMichael Riesch 	strscpy(rkcif->media_dev.model, RKCIF_DRIVER_NAME,
199f53fb31aSMichael Riesch 		sizeof(rkcif->media_dev.model));
200f53fb31aSMichael Riesch 	media_device_init(&rkcif->media_dev);
201f53fb31aSMichael Riesch 
202f53fb31aSMichael Riesch 	rkcif->v4l2_dev.mdev = &rkcif->media_dev;
203f53fb31aSMichael Riesch 	ret = v4l2_device_register(dev, &rkcif->v4l2_dev);
204f53fb31aSMichael Riesch 	if (ret)
205f53fb31aSMichael Riesch 		goto err_media_dev_cleanup;
206f53fb31aSMichael Riesch 
207f53fb31aSMichael Riesch 	ret = media_device_register(&rkcif->media_dev);
208f53fb31aSMichael Riesch 	if (ret < 0) {
209f53fb31aSMichael Riesch 		dev_err(dev, "failed to register media device: %d\n", ret);
210f53fb31aSMichael Riesch 		goto err_v4l2_dev_unregister;
211f53fb31aSMichael Riesch 	}
212f53fb31aSMichael Riesch 
213f53fb31aSMichael Riesch 	v4l2_async_nf_init(&rkcif->notifier, &rkcif->v4l2_dev);
214f53fb31aSMichael Riesch 	rkcif->notifier.ops = &rkcif_notifier_ops;
215f53fb31aSMichael Riesch 
216f53fb31aSMichael Riesch 	ret = rkcif_register(rkcif);
217f53fb31aSMichael Riesch 	if (ret) {
218f53fb31aSMichael Riesch 		dev_err(dev, "failed to register media entities: %d\n", ret);
219f53fb31aSMichael Riesch 		goto err_notifier_cleanup;
220f53fb31aSMichael Riesch 	}
221f53fb31aSMichael Riesch 
222f53fb31aSMichael Riesch 	ret = v4l2_async_nf_register(&rkcif->notifier);
223f53fb31aSMichael Riesch 	if (ret)
224f53fb31aSMichael Riesch 		goto err_rkcif_unregister;
225f53fb31aSMichael Riesch 
226f53fb31aSMichael Riesch 	return 0;
227f53fb31aSMichael Riesch 
228f53fb31aSMichael Riesch err_rkcif_unregister:
229f53fb31aSMichael Riesch 	rkcif_unregister(rkcif);
230f53fb31aSMichael Riesch err_notifier_cleanup:
231f53fb31aSMichael Riesch 	v4l2_async_nf_cleanup(&rkcif->notifier);
232f53fb31aSMichael Riesch 	media_device_unregister(&rkcif->media_dev);
233f53fb31aSMichael Riesch err_v4l2_dev_unregister:
234f53fb31aSMichael Riesch 	v4l2_device_unregister(&rkcif->v4l2_dev);
235f53fb31aSMichael Riesch err_media_dev_cleanup:
236f53fb31aSMichael Riesch 	media_device_cleanup(&rkcif->media_dev);
237f53fb31aSMichael Riesch 	pm_runtime_disable(&pdev->dev);
238f53fb31aSMichael Riesch 	return ret;
239f53fb31aSMichael Riesch }
240f53fb31aSMichael Riesch 
241f53fb31aSMichael Riesch static void rkcif_remove(struct platform_device *pdev)
242f53fb31aSMichael Riesch {
243f53fb31aSMichael Riesch 	struct rkcif_device *rkcif = platform_get_drvdata(pdev);
244f53fb31aSMichael Riesch 
245f53fb31aSMichael Riesch 	v4l2_async_nf_unregister(&rkcif->notifier);
246f53fb31aSMichael Riesch 	rkcif_unregister(rkcif);
247f53fb31aSMichael Riesch 	v4l2_async_nf_cleanup(&rkcif->notifier);
248f53fb31aSMichael Riesch 	media_device_unregister(&rkcif->media_dev);
249f53fb31aSMichael Riesch 	v4l2_device_unregister(&rkcif->v4l2_dev);
250f53fb31aSMichael Riesch 	media_device_cleanup(&rkcif->media_dev);
251f53fb31aSMichael Riesch 	pm_runtime_disable(&pdev->dev);
252f53fb31aSMichael Riesch }
253f53fb31aSMichael Riesch 
254f53fb31aSMichael Riesch static int rkcif_runtime_suspend(struct device *dev)
255f53fb31aSMichael Riesch {
256f53fb31aSMichael Riesch 	struct rkcif_device *rkcif = dev_get_drvdata(dev);
257f53fb31aSMichael Riesch 
258f53fb31aSMichael Riesch 	/*
259f53fb31aSMichael Riesch 	 * Reset CIF (CRU, DMA, FIFOs) to allow a clean resume.
260f53fb31aSMichael Riesch 	 * Since this resets the IOMMU too, we cannot issue this reset when
261f53fb31aSMichael Riesch 	 * resuming.
262f53fb31aSMichael Riesch 	 */
263f53fb31aSMichael Riesch 	reset_control_assert(rkcif->reset);
264f53fb31aSMichael Riesch 	udelay(5);
265f53fb31aSMichael Riesch 	reset_control_deassert(rkcif->reset);
266f53fb31aSMichael Riesch 
267f53fb31aSMichael Riesch 	clk_bulk_disable_unprepare(rkcif->clks_num, rkcif->clks);
268f53fb31aSMichael Riesch 
269f53fb31aSMichael Riesch 	return 0;
270f53fb31aSMichael Riesch }
271f53fb31aSMichael Riesch 
272f53fb31aSMichael Riesch static int rkcif_runtime_resume(struct device *dev)
273f53fb31aSMichael Riesch {
274f53fb31aSMichael Riesch 	struct rkcif_device *rkcif = dev_get_drvdata(dev);
275f53fb31aSMichael Riesch 	int ret;
276f53fb31aSMichael Riesch 
277f53fb31aSMichael Riesch 	ret = clk_bulk_prepare_enable(rkcif->clks_num, rkcif->clks);
278f53fb31aSMichael Riesch 	if (ret) {
279f53fb31aSMichael Riesch 		dev_err(dev, "failed to enable clocks\n");
280f53fb31aSMichael Riesch 		return ret;
281f53fb31aSMichael Riesch 	}
282f53fb31aSMichael Riesch 
283f53fb31aSMichael Riesch 	return 0;
284f53fb31aSMichael Riesch }
285f53fb31aSMichael Riesch 
286f53fb31aSMichael Riesch static const struct dev_pm_ops rkcif_plat_pm_ops = {
287f53fb31aSMichael Riesch 	.runtime_suspend = rkcif_runtime_suspend,
288f53fb31aSMichael Riesch 	.runtime_resume = rkcif_runtime_resume,
289f53fb31aSMichael Riesch };
290f53fb31aSMichael Riesch 
291f53fb31aSMichael Riesch static struct platform_driver rkcif_plat_drv = {
292f53fb31aSMichael Riesch 	.driver = {
293f53fb31aSMichael Riesch 		   .name = RKCIF_DRIVER_NAME,
294f53fb31aSMichael Riesch 		   .of_match_table = rkcif_plat_of_match,
295f53fb31aSMichael Riesch 		   .pm = &rkcif_plat_pm_ops,
296f53fb31aSMichael Riesch 	},
297f53fb31aSMichael Riesch 	.probe = rkcif_probe,
298f53fb31aSMichael Riesch 	.remove = rkcif_remove,
299f53fb31aSMichael Riesch };
300f53fb31aSMichael Riesch module_platform_driver(rkcif_plat_drv);
301f53fb31aSMichael Riesch 
302f53fb31aSMichael Riesch MODULE_DESCRIPTION("Rockchip Camera Interface (CIF) platform driver");
303f53fb31aSMichael Riesch MODULE_LICENSE("GPL");
304