Skip to content

Commit f53fb31

Browse files
Michael RieschHans Verkuil
authored andcommitted
media: rockchip: add driver for the rockchip camera interface
Add the skeleton for a media controller centric V4L2 driver for the Rockchip Camera Interface (CIF). The skeleton features support for the PX30 Video Input Processor (VIP) and the RK3568 Video Capture (VICAP) unit. Tested-by: Gerald Loacker <gerald.loacker@wolfvision.net> Reviewed-by: Gerald Loacker <gerald.loacker@wolfvision.net> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Mehdi Djait <mehdi.djait@linux.intel.com> Signed-off-by: Michael Riesch <michael.riesch@collabora.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
1 parent 60836ee commit f53fb31

7 files changed

Lines changed: 342 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22271,6 +22271,7 @@ S: Maintained
2227122271
F: Documentation/admin-guide/media/rkcif*
2227222272
F: Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml
2227322273
F: Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
22274+
F: drivers/media/platform/rockchip/rkcif/
2227422275

2227522276
ROCKCHIP CRYPTO DRIVERS
2227622277
M: Corentin Labbe <clabbe@baylibre.com>

drivers/media/platform/rockchip/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
comment "Rockchip media platform drivers"
44

55
source "drivers/media/platform/rockchip/rga/Kconfig"
6+
source "drivers/media/platform/rockchip/rkcif/Kconfig"
67
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
78
source "drivers/media/platform/rockchip/rkvdec/Kconfig"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
obj-y += rga/
3+
obj-y += rkcif/
34
obj-y += rkisp1/
45
obj-y += rkvdec/
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
config VIDEO_ROCKCHIP_CIF
2+
tristate "Rockchip Camera Interface (CIF)"
3+
depends on VIDEO_DEV
4+
depends on ARCH_ROCKCHIP || COMPILE_TEST
5+
depends on V4L_PLATFORM_DRIVERS
6+
depends on PM && COMMON_CLK
7+
select MEDIA_CONTROLLER
8+
select VIDEOBUF2_DMA_CONTIG
9+
select V4L2_FWNODE
10+
select VIDEO_V4L2_SUBDEV_API
11+
help
12+
This is a driver for Rockchip Camera Interface (CIF). It is featured
13+
in many Rockchips SoCs in different variations, such as the PX30
14+
Video Input Processor (VIP, one Digital Video Port (DVP)) or the
15+
RK3568 Video Capture (VICAP, one DVP, one MIPI CSI-2 receiver) unit.
16+
17+
To compile this driver as a module, choose M here: the module
18+
will be called rockchip-cif.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o
3+
4+
rockchip-cif-objs += rkcif-dev.o
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Rockchip Camera Interface (CIF) Driver
4+
*
5+
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
6+
* Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
7+
* Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
8+
* Copyright (C) 2025 Collabora, Ltd.
9+
*/
10+
11+
#ifndef _RKCIF_COMMON_H
12+
#define _RKCIF_COMMON_H
13+
14+
#include <linux/clk.h>
15+
#include <linux/mutex.h>
16+
#include <linux/regmap.h>
17+
18+
#include <media/media-device.h>
19+
#include <media/media-entity.h>
20+
#include <media/v4l2-common.h>
21+
#include <media/v4l2-device.h>
22+
#include <media/v4l2-fwnode.h>
23+
#include <media/v4l2-mc.h>
24+
#include <media/v4l2-subdev.h>
25+
#include <media/videobuf2-v4l2.h>
26+
27+
#define RKCIF_DRIVER_NAME "rockchip-cif"
28+
#define RKCIF_CLK_MAX 4
29+
30+
struct rkcif_remote {
31+
struct v4l2_async_connection async_conn;
32+
struct v4l2_subdev *sd;
33+
};
34+
35+
struct rkcif_match_data {
36+
const char *const *clks;
37+
unsigned int clks_num;
38+
};
39+
40+
struct rkcif_device {
41+
struct device *dev;
42+
43+
const struct rkcif_match_data *match_data;
44+
struct clk_bulk_data clks[RKCIF_CLK_MAX];
45+
unsigned int clks_num;
46+
struct regmap *grf;
47+
struct reset_control *reset;
48+
void __iomem *base_addr;
49+
50+
struct media_device media_dev;
51+
struct v4l2_device v4l2_dev;
52+
struct v4l2_async_notifier notifier;
53+
};
54+
55+
#endif
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Rockchip Camera Interface (CIF) Driver
4+
*
5+
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
6+
* Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
7+
* Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
8+
* Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
9+
* Copyright (C) 2025 Collabora, Ltd.
10+
*/
11+
12+
#include <linux/clk.h>
13+
#include <linux/delay.h>
14+
#include <linux/interrupt.h>
15+
#include <linux/mfd/syscon.h>
16+
#include <linux/module.h>
17+
#include <linux/of.h>
18+
#include <linux/of_graph.h>
19+
#include <linux/of_platform.h>
20+
#include <linux/pm_runtime.h>
21+
#include <linux/reset.h>
22+
23+
#include <media/v4l2-fwnode.h>
24+
#include <media/v4l2-mc.h>
25+
26+
#include "rkcif-common.h"
27+
28+
static const char *const px30_vip_clks[] = {
29+
"aclk",
30+
"hclk",
31+
"pclk",
32+
};
33+
34+
static const struct rkcif_match_data px30_vip_match_data = {
35+
.clks = px30_vip_clks,
36+
.clks_num = ARRAY_SIZE(px30_vip_clks),
37+
};
38+
39+
static const char *const rk3568_vicap_clks[] = {
40+
"aclk",
41+
"hclk",
42+
"dclk",
43+
"iclk",
44+
};
45+
46+
static const struct rkcif_match_data rk3568_vicap_match_data = {
47+
.clks = rk3568_vicap_clks,
48+
.clks_num = ARRAY_SIZE(rk3568_vicap_clks),
49+
};
50+
51+
static const struct of_device_id rkcif_plat_of_match[] = {
52+
{
53+
.compatible = "rockchip,px30-vip",
54+
.data = &px30_vip_match_data,
55+
},
56+
{
57+
.compatible = "rockchip,rk3568-vicap",
58+
.data = &rk3568_vicap_match_data,
59+
},
60+
{}
61+
};
62+
MODULE_DEVICE_TABLE(of, rkcif_plat_of_match);
63+
64+
static int rkcif_register(struct rkcif_device *rkcif)
65+
{
66+
return 0;
67+
}
68+
69+
static void rkcif_unregister(struct rkcif_device *rkcif)
70+
{
71+
}
72+
73+
static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier,
74+
struct v4l2_subdev *sd,
75+
struct v4l2_async_connection *asd)
76+
{
77+
struct rkcif_remote *remote =
78+
container_of(asd, struct rkcif_remote, async_conn);
79+
80+
remote->sd = sd;
81+
82+
return 0;
83+
}
84+
85+
static int rkcif_notifier_complete(struct v4l2_async_notifier *notifier)
86+
{
87+
struct rkcif_device *rkcif =
88+
container_of(notifier, struct rkcif_device, notifier);
89+
90+
return v4l2_device_register_subdev_nodes(&rkcif->v4l2_dev);
91+
}
92+
93+
static const struct v4l2_async_notifier_operations rkcif_notifier_ops = {
94+
.bound = rkcif_notifier_bound,
95+
.complete = rkcif_notifier_complete,
96+
};
97+
98+
static irqreturn_t rkcif_isr(int irq, void *ctx)
99+
{
100+
irqreturn_t ret = IRQ_NONE;
101+
102+
return ret;
103+
}
104+
105+
static int rkcif_probe(struct platform_device *pdev)
106+
{
107+
struct device *dev = &pdev->dev;
108+
struct rkcif_device *rkcif;
109+
int ret, irq;
110+
111+
rkcif = devm_kzalloc(dev, sizeof(*rkcif), GFP_KERNEL);
112+
if (!rkcif)
113+
return -ENOMEM;
114+
115+
rkcif->match_data = of_device_get_match_data(dev);
116+
if (!rkcif->match_data)
117+
return -ENODEV;
118+
119+
dev_set_drvdata(dev, rkcif);
120+
rkcif->dev = dev;
121+
122+
rkcif->base_addr = devm_platform_ioremap_resource(pdev, 0);
123+
if (IS_ERR(rkcif->base_addr))
124+
return PTR_ERR(rkcif->base_addr);
125+
126+
irq = platform_get_irq(pdev, 0);
127+
if (irq < 0)
128+
return irq;
129+
130+
ret = devm_request_irq(dev, irq, rkcif_isr, IRQF_SHARED,
131+
dev_driver_string(dev), dev);
132+
if (ret)
133+
return dev_err_probe(dev, ret, "failed to request irq\n");
134+
135+
if (rkcif->match_data->clks_num > RKCIF_CLK_MAX)
136+
return dev_err_probe(dev, -EINVAL, "invalid number of clocks\n");
137+
138+
rkcif->clks_num = rkcif->match_data->clks_num;
139+
for (unsigned int i = 0; i < rkcif->clks_num; i++)
140+
rkcif->clks[i].id = rkcif->match_data->clks[i];
141+
ret = devm_clk_bulk_get(dev, rkcif->clks_num, rkcif->clks);
142+
if (ret)
143+
return dev_err_probe(dev, ret, "failed to get clocks\n");
144+
145+
rkcif->reset = devm_reset_control_array_get_exclusive(dev);
146+
if (IS_ERR(rkcif->reset))
147+
return PTR_ERR(rkcif->reset);
148+
149+
rkcif->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
150+
"rockchip,grf");
151+
if (IS_ERR(rkcif->grf))
152+
rkcif->grf = NULL;
153+
154+
pm_runtime_enable(&pdev->dev);
155+
156+
rkcif->media_dev.dev = dev;
157+
strscpy(rkcif->media_dev.model, RKCIF_DRIVER_NAME,
158+
sizeof(rkcif->media_dev.model));
159+
media_device_init(&rkcif->media_dev);
160+
161+
rkcif->v4l2_dev.mdev = &rkcif->media_dev;
162+
ret = v4l2_device_register(dev, &rkcif->v4l2_dev);
163+
if (ret)
164+
goto err_media_dev_cleanup;
165+
166+
ret = media_device_register(&rkcif->media_dev);
167+
if (ret < 0) {
168+
dev_err(dev, "failed to register media device: %d\n", ret);
169+
goto err_v4l2_dev_unregister;
170+
}
171+
172+
v4l2_async_nf_init(&rkcif->notifier, &rkcif->v4l2_dev);
173+
rkcif->notifier.ops = &rkcif_notifier_ops;
174+
175+
ret = rkcif_register(rkcif);
176+
if (ret) {
177+
dev_err(dev, "failed to register media entities: %d\n", ret);
178+
goto err_notifier_cleanup;
179+
}
180+
181+
ret = v4l2_async_nf_register(&rkcif->notifier);
182+
if (ret)
183+
goto err_rkcif_unregister;
184+
185+
return 0;
186+
187+
err_rkcif_unregister:
188+
rkcif_unregister(rkcif);
189+
err_notifier_cleanup:
190+
v4l2_async_nf_cleanup(&rkcif->notifier);
191+
media_device_unregister(&rkcif->media_dev);
192+
err_v4l2_dev_unregister:
193+
v4l2_device_unregister(&rkcif->v4l2_dev);
194+
err_media_dev_cleanup:
195+
media_device_cleanup(&rkcif->media_dev);
196+
pm_runtime_disable(&pdev->dev);
197+
return ret;
198+
}
199+
200+
static void rkcif_remove(struct platform_device *pdev)
201+
{
202+
struct rkcif_device *rkcif = platform_get_drvdata(pdev);
203+
204+
v4l2_async_nf_unregister(&rkcif->notifier);
205+
rkcif_unregister(rkcif);
206+
v4l2_async_nf_cleanup(&rkcif->notifier);
207+
media_device_unregister(&rkcif->media_dev);
208+
v4l2_device_unregister(&rkcif->v4l2_dev);
209+
media_device_cleanup(&rkcif->media_dev);
210+
pm_runtime_disable(&pdev->dev);
211+
}
212+
213+
static int rkcif_runtime_suspend(struct device *dev)
214+
{
215+
struct rkcif_device *rkcif = dev_get_drvdata(dev);
216+
217+
/*
218+
* Reset CIF (CRU, DMA, FIFOs) to allow a clean resume.
219+
* Since this resets the IOMMU too, we cannot issue this reset when
220+
* resuming.
221+
*/
222+
reset_control_assert(rkcif->reset);
223+
udelay(5);
224+
reset_control_deassert(rkcif->reset);
225+
226+
clk_bulk_disable_unprepare(rkcif->clks_num, rkcif->clks);
227+
228+
return 0;
229+
}
230+
231+
static int rkcif_runtime_resume(struct device *dev)
232+
{
233+
struct rkcif_device *rkcif = dev_get_drvdata(dev);
234+
int ret;
235+
236+
ret = clk_bulk_prepare_enable(rkcif->clks_num, rkcif->clks);
237+
if (ret) {
238+
dev_err(dev, "failed to enable clocks\n");
239+
return ret;
240+
}
241+
242+
return 0;
243+
}
244+
245+
static const struct dev_pm_ops rkcif_plat_pm_ops = {
246+
.runtime_suspend = rkcif_runtime_suspend,
247+
.runtime_resume = rkcif_runtime_resume,
248+
};
249+
250+
static struct platform_driver rkcif_plat_drv = {
251+
.driver = {
252+
.name = RKCIF_DRIVER_NAME,
253+
.of_match_table = rkcif_plat_of_match,
254+
.pm = &rkcif_plat_pm_ops,
255+
},
256+
.probe = rkcif_probe,
257+
.remove = rkcif_remove,
258+
};
259+
module_platform_driver(rkcif_plat_drv);
260+
261+
MODULE_DESCRIPTION("Rockchip Camera Interface (CIF) platform driver");
262+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)