1e6938cc1SHelen Koike // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2e6938cc1SHelen Koike /*
3e6938cc1SHelen Koike * Rockchip ISP1 Driver - Base driver
4e6938cc1SHelen Koike *
5e6938cc1SHelen Koike * Copyright (C) 2019 Collabora, Ltd.
6e6938cc1SHelen Koike *
7e6938cc1SHelen Koike * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
8e6938cc1SHelen Koike * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
9e6938cc1SHelen Koike */
10e6938cc1SHelen Koike
11e6938cc1SHelen Koike #include <linux/clk.h>
12e6938cc1SHelen Koike #include <linux/interrupt.h>
13fdac4ce9SLaurent Pinchart #include <linux/mfd/syscon.h>
14e6938cc1SHelen Koike #include <linux/module.h>
15e6938cc1SHelen Koike #include <linux/of.h>
16e6938cc1SHelen Koike #include <linux/of_graph.h>
177c7e33b7SRob Herring #include <linux/platform_device.h>
18e6938cc1SHelen Koike #include <linux/pinctrl/consumer.h>
198082e2f4SPaul Elder #include <linux/pm_runtime.h>
20e6938cc1SHelen Koike #include <media/v4l2-fwnode.h>
2198bfd0cdSLaurent Pinchart #include <media/v4l2-mc.h>
22e6938cc1SHelen Koike
23e6938cc1SHelen Koike #include "rkisp1-common.h"
248082e2f4SPaul Elder #include "rkisp1-csi.h"
25e6938cc1SHelen Koike
26e6938cc1SHelen Koike /*
27e6938cc1SHelen Koike * ISP Details
28e6938cc1SHelen Koike * -----------
29e6938cc1SHelen Koike *
30e6938cc1SHelen Koike * ISP Comprises with:
31e6938cc1SHelen Koike * MIPI serial camera interface
32e6938cc1SHelen Koike * Image Signal Processing
33e6938cc1SHelen Koike * Many Image Enhancement Blocks
34e6938cc1SHelen Koike * Crop
35e6938cc1SHelen Koike * Resizer
36e6938cc1SHelen Koike * RBG display ready image
37e6938cc1SHelen Koike * Image Rotation
38e6938cc1SHelen Koike *
39e6938cc1SHelen Koike * ISP Block Diagram
40e6938cc1SHelen Koike * -----------------
41e6938cc1SHelen Koike * rkisp1-resizer.c rkisp1-capture.c
42e6938cc1SHelen Koike * |====================| |=======================|
43e6938cc1SHelen Koike * rkisp1-isp.c Main Picture Path
44e6938cc1SHelen Koike * |==========================| |===============================================|
45e6938cc1SHelen Koike * +-----------+ +--+--+--+--+ +--------+ +--------+ +-----------+
46e6938cc1SHelen Koike * | | | | | | | | | | | | |
47e6938cc1SHelen Koike * +--------+ |\ | | | | | | | -->| Crop |->| RSZ |------------->| |
48e6938cc1SHelen Koike * | MIPI |--->| \ | | | | | | | | | | | | | |
49e6938cc1SHelen Koike * +--------+ | | | | |IE|IE|IE|IE| | +--------+ +--------+ | Memory |
50e6938cc1SHelen Koike * |MUX|--->| ISP |->|0 |1 |2 |3 |---+ | Interface |
51e6938cc1SHelen Koike * +--------+ | | | | | | | | | | +--------+ +--------+ +--------+ | |
52e6938cc1SHelen Koike * |Parallel|--->| / | | | | | | | | | | | | | | | |
53e6938cc1SHelen Koike * +--------+ |/ | | | | | | | -->| Crop |->| RSZ |->| RGB |->| |
54e6938cc1SHelen Koike * | | | | | | | | | | | | Rotate | | |
55e6938cc1SHelen Koike * +-----------+ +--+--+--+--+ +--------+ +--------+ +--------+ +-----------+
56e6938cc1SHelen Koike * ^
57e6938cc1SHelen Koike * +--------+ | |===============================================|
58e6938cc1SHelen Koike * | DMA |------------------------------------+ Self Picture Path
59e6938cc1SHelen Koike * +--------+
60e6938cc1SHelen Koike *
61e6938cc1SHelen Koike * rkisp1-stats.c rkisp1-params.c
62e6938cc1SHelen Koike * |===============| |===============|
63e6938cc1SHelen Koike * +---------------+ +---------------+
64e6938cc1SHelen Koike * | | | |
65e6938cc1SHelen Koike * | ISP | | ISP |
66e6938cc1SHelen Koike * | | | |
67e6938cc1SHelen Koike * +---------------+ +---------------+
68e6938cc1SHelen Koike *
69e6938cc1SHelen Koike *
70e6938cc1SHelen Koike * Media Topology
71e6938cc1SHelen Koike * --------------
7298bfd0cdSLaurent Pinchart *
73e6938cc1SHelen Koike * +----------+ +----------+
7498bfd0cdSLaurent Pinchart * | Sensor 1 | | Sensor X |
75e6938cc1SHelen Koike * ------------ ... ------------
76e6938cc1SHelen Koike * | 0 | | 0 |
7798bfd0cdSLaurent Pinchart * +----------+ +----------+
7898bfd0cdSLaurent Pinchart * | |
7998bfd0cdSLaurent Pinchart * \----\ /----/
8098bfd0cdSLaurent Pinchart * | |
8198bfd0cdSLaurent Pinchart * v v
8298bfd0cdSLaurent Pinchart * +-------------+
8398bfd0cdSLaurent Pinchart * | 0 |
8498bfd0cdSLaurent Pinchart * ---------------
8598bfd0cdSLaurent Pinchart * | CSI-2 RX |
8698bfd0cdSLaurent Pinchart * --------------- +-----------+
8798bfd0cdSLaurent Pinchart * | 1 | | params |
8898bfd0cdSLaurent Pinchart * +-------------+ | (output) |
8998bfd0cdSLaurent Pinchart * | +-----------+
9098bfd0cdSLaurent Pinchart * v |
9198bfd0cdSLaurent Pinchart * +------+------+ |
9298bfd0cdSLaurent Pinchart * | 0 | 1 |<---------+
9398bfd0cdSLaurent Pinchart * |------+------|
94e6938cc1SHelen Koike * | ISP |
95e6938cc1SHelen Koike * |------+------|
96e6938cc1SHelen Koike * +-------------| 2 | 3 |----------+
97e6938cc1SHelen Koike * | +------+------+ |
98e6938cc1SHelen Koike * | | |
99e6938cc1SHelen Koike * v v v
100e6938cc1SHelen Koike * +- ---------+ +-----------+ +-----------+
101e6938cc1SHelen Koike * | 0 | | 0 | | stats |
102e6938cc1SHelen Koike * ------------- ------------- | (capture) |
103e6938cc1SHelen Koike * | Resizer | | Resizer | +-----------+
104e6938cc1SHelen Koike * ------------| ------------|
105e6938cc1SHelen Koike * | 1 | | 1 |
106e6938cc1SHelen Koike * +-----------+ +-----------+
107e6938cc1SHelen Koike * | |
108e6938cc1SHelen Koike * v v
109e6938cc1SHelen Koike * +-----------+ +-----------+
110e6938cc1SHelen Koike * | selfpath | | mainpath |
111e6938cc1SHelen Koike * | (capture) | | (capture) |
112e6938cc1SHelen Koike * +-----------+ +-----------+
113e6938cc1SHelen Koike */
114e6938cc1SHelen Koike
11508818e6aSHeiko Stuebner struct rkisp1_isr_data {
11608818e6aSHeiko Stuebner const char *name;
11708818e6aSHeiko Stuebner irqreturn_t (*isr)(int irq, void *ctx);
11807538746STomi Valkeinen u32 line_mask;
11908818e6aSHeiko Stuebner };
12008818e6aSHeiko Stuebner
121e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
122e6938cc1SHelen Koike * Sensor DT bindings
123e6938cc1SHelen Koike */
124e6938cc1SHelen Koike
rkisp1_subdev_notifier_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * sd,struct v4l2_async_connection * asc)125e6938cc1SHelen Koike static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
126e6938cc1SHelen Koike struct v4l2_subdev *sd,
127adb2dcd5SSakari Ailus struct v4l2_async_connection *asc)
128e6938cc1SHelen Koike {
129e6938cc1SHelen Koike struct rkisp1_device *rkisp1 =
130e6938cc1SHelen Koike container_of(notifier, struct rkisp1_device, notifier);
131e6938cc1SHelen Koike struct rkisp1_sensor_async *s_asd =
132adb2dcd5SSakari Ailus container_of(asc, struct rkisp1_sensor_async, asd);
133bc374e17SLaurent Pinchart int source_pad;
134f42f4558SPaul Elder int ret;
135e6938cc1SHelen Koike
136e6938cc1SHelen Koike s_asd->sd = sd;
137e6938cc1SHelen Koike
138b0b8ab68SLaurent Pinchart source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep,
139bc374e17SLaurent Pinchart MEDIA_PAD_FL_SOURCE);
140bc374e17SLaurent Pinchart if (source_pad < 0) {
141bc374e17SLaurent Pinchart dev_err(rkisp1->dev, "failed to find source pad for %s\n",
142bc374e17SLaurent Pinchart sd->name);
143bc374e17SLaurent Pinchart return source_pad;
144bc374e17SLaurent Pinchart }
145bc374e17SLaurent Pinchart
146f42f4558SPaul Elder if (s_asd->port == 0)
14798bfd0cdSLaurent Pinchart return rkisp1_csi_link_sensor(rkisp1, sd, s_asd, source_pad);
148f42f4558SPaul Elder
149f42f4558SPaul Elder ret = media_create_pad_link(&sd->entity, source_pad,
150f42f4558SPaul Elder &rkisp1->isp.sd.entity,
151f42f4558SPaul Elder RKISP1_ISP_PAD_SINK_VIDEO,
152f42f4558SPaul Elder !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0);
153f42f4558SPaul Elder if (ret) {
154f42f4558SPaul Elder dev_err(rkisp1->dev, "failed to link source pad of %s\n",
155f42f4558SPaul Elder sd->name);
156f42f4558SPaul Elder return ret;
157f42f4558SPaul Elder }
158f42f4558SPaul Elder
159f42f4558SPaul Elder return 0;
160e6938cc1SHelen Koike }
161e6938cc1SHelen Koike
rkisp1_subdev_notifier_complete(struct v4l2_async_notifier * notifier)162e6938cc1SHelen Koike static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
163e6938cc1SHelen Koike {
164e6938cc1SHelen Koike struct rkisp1_device *rkisp1 =
165e6938cc1SHelen Koike container_of(notifier, struct rkisp1_device, notifier);
166e6938cc1SHelen Koike
167cf7a8e24SLaurent Pinchart return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev);
168e6938cc1SHelen Koike }
169e6938cc1SHelen Koike
rkisp1_subdev_notifier_destroy(struct v4l2_async_connection * asc)170adb2dcd5SSakari Ailus static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc)
171b0b8ab68SLaurent Pinchart {
172b0b8ab68SLaurent Pinchart struct rkisp1_sensor_async *rk_asd =
173adb2dcd5SSakari Ailus container_of(asc, struct rkisp1_sensor_async, asd);
174b0b8ab68SLaurent Pinchart
175b0b8ab68SLaurent Pinchart fwnode_handle_put(rk_asd->source_ep);
176b0b8ab68SLaurent Pinchart }
177b0b8ab68SLaurent Pinchart
178e6938cc1SHelen Koike static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
179e6938cc1SHelen Koike .bound = rkisp1_subdev_notifier_bound,
180e6938cc1SHelen Koike .complete = rkisp1_subdev_notifier_complete,
181b0b8ab68SLaurent Pinchart .destroy = rkisp1_subdev_notifier_destroy,
182e6938cc1SHelen Koike };
183e6938cc1SHelen Koike
rkisp1_subdev_notifier_register(struct rkisp1_device * rkisp1)184124b89f8SLaurent Pinchart static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
185e6938cc1SHelen Koike {
186e6938cc1SHelen Koike struct v4l2_async_notifier *ntf = &rkisp1->notifier;
1872452171eSPaul Elder struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev);
1882452171eSPaul Elder struct fwnode_handle *ep;
189bc374e17SLaurent Pinchart unsigned int index = 0;
1902452171eSPaul Elder int ret = 0;
191e6938cc1SHelen Koike
192b8ec754aSSakari Ailus v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev);
193e6938cc1SHelen Koike
1942452171eSPaul Elder ntf->ops = &rkisp1_subdev_notifier_ops;
1952452171eSPaul Elder
1962452171eSPaul Elder fwnode_graph_for_each_endpoint(fwnode, ep) {
197f42f4558SPaul Elder struct fwnode_handle *port;
198f42f4558SPaul Elder struct v4l2_fwnode_endpoint vep = { };
199b01edcbdSLaurent Pinchart struct rkisp1_sensor_async *rk_asd;
2002452171eSPaul Elder struct fwnode_handle *source;
201f42f4558SPaul Elder u32 reg = 0;
202e6938cc1SHelen Koike
203f42f4558SPaul Elder /* Select the bus type based on the port. */
204f42f4558SPaul Elder port = fwnode_get_parent(ep);
205f42f4558SPaul Elder fwnode_property_read_u32(port, "reg", ®);
206f42f4558SPaul Elder fwnode_handle_put(port);
207f42f4558SPaul Elder
208f42f4558SPaul Elder switch (reg) {
209f42f4558SPaul Elder case 0:
2107d4f126fSLaurent Pinchart /* MIPI CSI-2 port */
211900f6676SLaurent Pinchart if (!rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
2127d4f126fSLaurent Pinchart dev_err(rkisp1->dev,
2137d4f126fSLaurent Pinchart "internal CSI must be available for port 0\n");
2147d4f126fSLaurent Pinchart ret = -EINVAL;
2157d4f126fSLaurent Pinchart break;
2167d4f126fSLaurent Pinchart }
2177d4f126fSLaurent Pinchart
218f42f4558SPaul Elder vep.bus_type = V4L2_MBUS_CSI2_DPHY;
219f42f4558SPaul Elder break;
220f42f4558SPaul Elder
221f42f4558SPaul Elder case 1:
222f42f4558SPaul Elder /*
223f42f4558SPaul Elder * Parallel port. The bus-type property in DT is
224f42f4558SPaul Elder * mandatory for port 1, it will be used to determine if
225f42f4558SPaul Elder * it's PARALLEL or BT656.
226f42f4558SPaul Elder */
227f42f4558SPaul Elder vep.bus_type = V4L2_MBUS_UNKNOWN;
228f42f4558SPaul Elder break;
229f42f4558SPaul Elder }
230f42f4558SPaul Elder
231f42f4558SPaul Elder /* Parse the endpoint and validate the bus type. */
232e6938cc1SHelen Koike ret = v4l2_fwnode_endpoint_parse(ep, &vep);
2332452171eSPaul Elder if (ret) {
2342452171eSPaul Elder dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n",
2352452171eSPaul Elder ep);
2362452171eSPaul Elder break;
2372452171eSPaul Elder }
238e6938cc1SHelen Koike
239f42f4558SPaul Elder if (vep.base.port == 1) {
240f42f4558SPaul Elder if (vep.bus_type != V4L2_MBUS_PARALLEL &&
241f42f4558SPaul Elder vep.bus_type != V4L2_MBUS_BT656) {
242f42f4558SPaul Elder dev_err(rkisp1->dev,
243f42f4558SPaul Elder "port 1 must be parallel or BT656\n");
244f42f4558SPaul Elder ret = -EINVAL;
245f42f4558SPaul Elder break;
246f42f4558SPaul Elder }
247f42f4558SPaul Elder }
248f42f4558SPaul Elder
249f42f4558SPaul Elder /* Add the async subdev to the notifier. */
250b0b8ab68SLaurent Pinchart source = fwnode_graph_get_remote_endpoint(ep);
251b0b8ab68SLaurent Pinchart if (!source) {
252b0b8ab68SLaurent Pinchart dev_err(rkisp1->dev,
253b0b8ab68SLaurent Pinchart "endpoint %pfw has no remote endpoint\n",
254b0b8ab68SLaurent Pinchart ep);
255b0b8ab68SLaurent Pinchart ret = -ENODEV;
2562452171eSPaul Elder break;
257b0b8ab68SLaurent Pinchart }
258b0b8ab68SLaurent Pinchart
259b0b8ab68SLaurent Pinchart rk_asd = v4l2_async_nf_add_fwnode(ntf, source,
260b0b8ab68SLaurent Pinchart struct rkisp1_sensor_async);
261b01edcbdSLaurent Pinchart if (IS_ERR(rk_asd)) {
2622452171eSPaul Elder fwnode_handle_put(source);
263b01edcbdSLaurent Pinchart ret = PTR_ERR(rk_asd);
2642452171eSPaul Elder break;
265e6938cc1SHelen Koike }
266e6938cc1SHelen Koike
267bc374e17SLaurent Pinchart rk_asd->index = index++;
268b0b8ab68SLaurent Pinchart rk_asd->source_ep = source;
269e6938cc1SHelen Koike rk_asd->mbus_type = vep.bus_type;
270f42f4558SPaul Elder rk_asd->port = vep.base.port;
271f42f4558SPaul Elder
272f42f4558SPaul Elder if (vep.bus_type == V4L2_MBUS_CSI2_DPHY) {
273e6938cc1SHelen Koike rk_asd->mbus_flags = vep.bus.mipi_csi2.flags;
274e6938cc1SHelen Koike rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes;
275f42f4558SPaul Elder } else {
276f42f4558SPaul Elder rk_asd->mbus_flags = vep.bus.parallel.flags;
277f42f4558SPaul Elder }
278e6938cc1SHelen Koike
279f42f4558SPaul Elder dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n",
280f42f4558SPaul Elder vep.base.id, rk_asd->mbus_type, rk_asd->lanes);
2812452171eSPaul Elder }
282e6938cc1SHelen Koike
2832452171eSPaul Elder if (ret) {
284e6938cc1SHelen Koike fwnode_handle_put(ep);
2853c8c1539SSakari Ailus v4l2_async_nf_cleanup(ntf);
286e6938cc1SHelen Koike return ret;
287e6938cc1SHelen Koike }
288e6938cc1SHelen Koike
2892452171eSPaul Elder if (!index)
290e6938cc1SHelen Koike dev_dbg(rkisp1->dev, "no remote subdevice found\n");
2912452171eSPaul Elder
292b8ec754aSSakari Ailus ret = v4l2_async_nf_register(ntf);
293e6938cc1SHelen Koike if (ret) {
2943c8c1539SSakari Ailus v4l2_async_nf_cleanup(ntf);
295e6938cc1SHelen Koike return ret;
296e6938cc1SHelen Koike }
2972452171eSPaul Elder
298e6938cc1SHelen Koike return 0;
299e6938cc1SHelen Koike }
300e6938cc1SHelen Koike
301e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
302e6938cc1SHelen Koike * Power
303e6938cc1SHelen Koike */
304e6938cc1SHelen Koike
rkisp1_runtime_suspend(struct device * dev)305e6938cc1SHelen Koike static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
306e6938cc1SHelen Koike {
307e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
308e6938cc1SHelen Koike
309ffb635bbSTomi Valkeinen rkisp1->irqs_enabled = false;
310ffb635bbSTomi Valkeinen /* Make sure the IRQ handler will see the above */
311ffb635bbSTomi Valkeinen mb();
312ffb635bbSTomi Valkeinen
313ffb635bbSTomi Valkeinen /*
314ffb635bbSTomi Valkeinen * Wait until any running IRQ handler has returned. The IRQ handler
315ffb635bbSTomi Valkeinen * may get called even after this (as it's a shared interrupt line)
316ffb635bbSTomi Valkeinen * but the 'irqs_enabled' flag will make the handler return immediately.
317ffb635bbSTomi Valkeinen */
318ffb635bbSTomi Valkeinen for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) {
319ffb635bbSTomi Valkeinen if (rkisp1->irqs[il] == -1)
320ffb635bbSTomi Valkeinen continue;
321ffb635bbSTomi Valkeinen
322ffb635bbSTomi Valkeinen /* Skip if the irq line is the same as previous */
323ffb635bbSTomi Valkeinen if (il == 0 || rkisp1->irqs[il - 1] != rkisp1->irqs[il])
324ffb635bbSTomi Valkeinen synchronize_irq(rkisp1->irqs[il]);
325ffb635bbSTomi Valkeinen }
326ffb635bbSTomi Valkeinen
327e6938cc1SHelen Koike clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks);
328e6938cc1SHelen Koike return pinctrl_pm_select_sleep_state(dev);
329e6938cc1SHelen Koike }
330e6938cc1SHelen Koike
rkisp1_runtime_resume(struct device * dev)331e6938cc1SHelen Koike static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
332e6938cc1SHelen Koike {
333e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
334e6938cc1SHelen Koike int ret;
335e6938cc1SHelen Koike
336e6938cc1SHelen Koike ret = pinctrl_pm_select_default_state(dev);
337e6938cc1SHelen Koike if (ret)
338e6938cc1SHelen Koike return ret;
339e6938cc1SHelen Koike ret = clk_bulk_prepare_enable(rkisp1->clk_size, rkisp1->clks);
340e6938cc1SHelen Koike if (ret)
341e6938cc1SHelen Koike return ret;
342e6938cc1SHelen Koike
343ffb635bbSTomi Valkeinen rkisp1->irqs_enabled = true;
344ffb635bbSTomi Valkeinen /* Make sure the IRQ handler will see the above */
345ffb635bbSTomi Valkeinen mb();
346ffb635bbSTomi Valkeinen
347e6938cc1SHelen Koike return 0;
348e6938cc1SHelen Koike }
349e6938cc1SHelen Koike
350e6938cc1SHelen Koike static const struct dev_pm_ops rkisp1_pm_ops = {
351e6938cc1SHelen Koike SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
352e6938cc1SHelen Koike pm_runtime_force_resume)
353e6938cc1SHelen Koike SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL)
354e6938cc1SHelen Koike };
355e6938cc1SHelen Koike
356e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
357e6938cc1SHelen Koike * Core
358e6938cc1SHelen Koike */
359e6938cc1SHelen Koike
rkisp1_create_links(struct rkisp1_device * rkisp1)360cf7a8e24SLaurent Pinchart static int rkisp1_create_links(struct rkisp1_device *rkisp1)
361cf7a8e24SLaurent Pinchart {
36282754080SPaul Elder unsigned int dev_count = rkisp1_path_count(rkisp1);
363cf7a8e24SLaurent Pinchart unsigned int i;
364cf7a8e24SLaurent Pinchart int ret;
365cf7a8e24SLaurent Pinchart
366900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
36798bfd0cdSLaurent Pinchart /* Link the CSI receiver to the ISP. */
3687d4f126fSLaurent Pinchart ret = media_create_pad_link(&rkisp1->csi.sd.entity,
3697d4f126fSLaurent Pinchart RKISP1_CSI_PAD_SRC,
37098bfd0cdSLaurent Pinchart &rkisp1->isp.sd.entity,
37198bfd0cdSLaurent Pinchart RKISP1_ISP_PAD_SINK_VIDEO,
37298bfd0cdSLaurent Pinchart MEDIA_LNK_FL_ENABLED);
37398bfd0cdSLaurent Pinchart if (ret)
37498bfd0cdSLaurent Pinchart return ret;
3757d4f126fSLaurent Pinchart }
37698bfd0cdSLaurent Pinchart
377cf7a8e24SLaurent Pinchart /* create ISP->RSZ->CAP links */
37882754080SPaul Elder for (i = 0; i < dev_count; i++) {
379cf7a8e24SLaurent Pinchart struct media_entity *resizer =
380cf7a8e24SLaurent Pinchart &rkisp1->resizer_devs[i].sd.entity;
381cf7a8e24SLaurent Pinchart struct media_entity *capture =
382cf7a8e24SLaurent Pinchart &rkisp1->capture_devs[i].vnode.vdev.entity;
383cf7a8e24SLaurent Pinchart
384cf7a8e24SLaurent Pinchart ret = media_create_pad_link(&rkisp1->isp.sd.entity,
385cf7a8e24SLaurent Pinchart RKISP1_ISP_PAD_SOURCE_VIDEO,
386cf7a8e24SLaurent Pinchart resizer, RKISP1_RSZ_PAD_SINK,
387cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED);
388cf7a8e24SLaurent Pinchart if (ret)
389cf7a8e24SLaurent Pinchart return ret;
390cf7a8e24SLaurent Pinchart
391cf7a8e24SLaurent Pinchart ret = media_create_pad_link(resizer, RKISP1_RSZ_PAD_SRC,
392cf7a8e24SLaurent Pinchart capture, 0,
393cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED |
394cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE);
395cf7a8e24SLaurent Pinchart if (ret)
396cf7a8e24SLaurent Pinchart return ret;
397cf7a8e24SLaurent Pinchart }
398cf7a8e24SLaurent Pinchart
399cf7a8e24SLaurent Pinchart /* params links */
400cf7a8e24SLaurent Pinchart ret = media_create_pad_link(&rkisp1->params.vnode.vdev.entity, 0,
401cf7a8e24SLaurent Pinchart &rkisp1->isp.sd.entity,
402cf7a8e24SLaurent Pinchart RKISP1_ISP_PAD_SINK_PARAMS,
403cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED |
404cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE);
405cf7a8e24SLaurent Pinchart if (ret)
406cf7a8e24SLaurent Pinchart return ret;
407cf7a8e24SLaurent Pinchart
408cf7a8e24SLaurent Pinchart /* 3A stats links */
409cf7a8e24SLaurent Pinchart return media_create_pad_link(&rkisp1->isp.sd.entity,
410cf7a8e24SLaurent Pinchart RKISP1_ISP_PAD_SOURCE_STATS,
411cf7a8e24SLaurent Pinchart &rkisp1->stats.vnode.vdev.entity, 0,
412cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_ENABLED |
413cf7a8e24SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE);
414cf7a8e24SLaurent Pinchart }
415cf7a8e24SLaurent Pinchart
rkisp1_entities_unregister(struct rkisp1_device * rkisp1)4166ff02276SLaurent Pinchart static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1)
4176ff02276SLaurent Pinchart {
418900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
419b298f059SPaul Elder rkisp1_csi_unregister(rkisp1);
4206ff02276SLaurent Pinchart rkisp1_params_unregister(rkisp1);
4216ff02276SLaurent Pinchart rkisp1_stats_unregister(rkisp1);
4226ff02276SLaurent Pinchart rkisp1_capture_devs_unregister(rkisp1);
4236ff02276SLaurent Pinchart rkisp1_resizer_devs_unregister(rkisp1);
4246ff02276SLaurent Pinchart rkisp1_isp_unregister(rkisp1);
4256ff02276SLaurent Pinchart }
4266ff02276SLaurent Pinchart
rkisp1_entities_register(struct rkisp1_device * rkisp1)427e6938cc1SHelen Koike static int rkisp1_entities_register(struct rkisp1_device *rkisp1)
428e6938cc1SHelen Koike {
429e6938cc1SHelen Koike int ret;
430e6938cc1SHelen Koike
431e6938cc1SHelen Koike ret = rkisp1_isp_register(rkisp1);
432e6938cc1SHelen Koike if (ret)
4336ff02276SLaurent Pinchart goto error;
434e6938cc1SHelen Koike
435e6938cc1SHelen Koike ret = rkisp1_resizer_devs_register(rkisp1);
436e6938cc1SHelen Koike if (ret)
4376ff02276SLaurent Pinchart goto error;
438e6938cc1SHelen Koike
439e6938cc1SHelen Koike ret = rkisp1_capture_devs_register(rkisp1);
440e6938cc1SHelen Koike if (ret)
4416ff02276SLaurent Pinchart goto error;
442e6938cc1SHelen Koike
443e6938cc1SHelen Koike ret = rkisp1_stats_register(rkisp1);
444e6938cc1SHelen Koike if (ret)
4456ff02276SLaurent Pinchart goto error;
446e6938cc1SHelen Koike
447e6938cc1SHelen Koike ret = rkisp1_params_register(rkisp1);
448e6938cc1SHelen Koike if (ret)
4496ff02276SLaurent Pinchart goto error;
450e6938cc1SHelen Koike
451900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
452b298f059SPaul Elder ret = rkisp1_csi_register(rkisp1);
453b298f059SPaul Elder if (ret)
454b298f059SPaul Elder goto error;
4557d4f126fSLaurent Pinchart }
456b298f059SPaul Elder
457cf7a8e24SLaurent Pinchart ret = rkisp1_create_links(rkisp1);
458cf7a8e24SLaurent Pinchart if (ret)
459cf7a8e24SLaurent Pinchart goto error;
460cf7a8e24SLaurent Pinchart
461e6938cc1SHelen Koike return 0;
4626ff02276SLaurent Pinchart
4636ff02276SLaurent Pinchart error:
4646ff02276SLaurent Pinchart rkisp1_entities_unregister(rkisp1);
465e6938cc1SHelen Koike return ret;
466e6938cc1SHelen Koike }
467e6938cc1SHelen Koike
rkisp1_isr(int irq,void * ctx)468e6938cc1SHelen Koike static irqreturn_t rkisp1_isr(int irq, void *ctx)
469e6938cc1SHelen Koike {
4703eb7910eSTomi Valkeinen irqreturn_t ret = IRQ_NONE;
4713eb7910eSTomi Valkeinen
472e6938cc1SHelen Koike /*
473e6938cc1SHelen Koike * Call rkisp1_capture_isr() first to handle the frame that
474e6938cc1SHelen Koike * potentially completed using the current frame_sequence number before
475e6938cc1SHelen Koike * it is potentially incremented by rkisp1_isp_isr() in the vertical
476e6938cc1SHelen Koike * sync.
477e6938cc1SHelen Koike */
478e6938cc1SHelen Koike
4793eb7910eSTomi Valkeinen if (rkisp1_capture_isr(irq, ctx) == IRQ_HANDLED)
4803eb7910eSTomi Valkeinen ret = IRQ_HANDLED;
4813eb7910eSTomi Valkeinen
4823eb7910eSTomi Valkeinen if (rkisp1_isp_isr(irq, ctx) == IRQ_HANDLED)
4833eb7910eSTomi Valkeinen ret = IRQ_HANDLED;
4843eb7910eSTomi Valkeinen
4853eb7910eSTomi Valkeinen if (rkisp1_csi_isr(irq, ctx) == IRQ_HANDLED)
4863eb7910eSTomi Valkeinen ret = IRQ_HANDLED;
4873eb7910eSTomi Valkeinen
4883eb7910eSTomi Valkeinen return ret;
489e6938cc1SHelen Koike }
490e6938cc1SHelen Koike
491ecf8d36fSHeiko Stuebner static const char * const px30_isp_clks[] = {
492ecf8d36fSHeiko Stuebner "isp",
493ecf8d36fSHeiko Stuebner "aclk",
494ecf8d36fSHeiko Stuebner "hclk",
495ecf8d36fSHeiko Stuebner "pclk",
496ecf8d36fSHeiko Stuebner };
497ecf8d36fSHeiko Stuebner
498ecf8d36fSHeiko Stuebner static const struct rkisp1_isr_data px30_isp_isrs[] = {
49907538746STomi Valkeinen { "isp", rkisp1_isp_isr, BIT(RKISP1_IRQ_ISP) },
50007538746STomi Valkeinen { "mi", rkisp1_capture_isr, BIT(RKISP1_IRQ_MI) },
50107538746STomi Valkeinen { "mipi", rkisp1_csi_isr, BIT(RKISP1_IRQ_MIPI) },
502ecf8d36fSHeiko Stuebner };
503ecf8d36fSHeiko Stuebner
504cdce5b95SLaurent Pinchart static const struct rkisp1_info px30_isp_info = {
505ecf8d36fSHeiko Stuebner .clks = px30_isp_clks,
506ecf8d36fSHeiko Stuebner .clk_size = ARRAY_SIZE(px30_isp_clks),
507ecf8d36fSHeiko Stuebner .isrs = px30_isp_isrs,
508ecf8d36fSHeiko Stuebner .isr_size = ARRAY_SIZE(px30_isp_isrs),
509ecf8d36fSHeiko Stuebner .isp_ver = RKISP1_V12,
51082754080SPaul Elder .features = RKISP1_FEATURE_MIPI_CSI2
5112db6cad8SPaul Elder | RKISP1_FEATURE_SELF_PATH
5122db6cad8SPaul Elder | RKISP1_FEATURE_DUAL_CROP,
513ecf8d36fSHeiko Stuebner };
514ecf8d36fSHeiko Stuebner
515e6938cc1SHelen Koike static const char * const rk3399_isp_clks[] = {
516e6938cc1SHelen Koike "isp",
517e6938cc1SHelen Koike "aclk",
518e6938cc1SHelen Koike "hclk",
519e6938cc1SHelen Koike };
520e6938cc1SHelen Koike
52108818e6aSHeiko Stuebner static const struct rkisp1_isr_data rk3399_isp_isrs[] = {
52207538746STomi Valkeinen { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) | BIT(RKISP1_IRQ_MIPI) },
52308818e6aSHeiko Stuebner };
52408818e6aSHeiko Stuebner
525cdce5b95SLaurent Pinchart static const struct rkisp1_info rk3399_isp_info = {
526e6938cc1SHelen Koike .clks = rk3399_isp_clks,
52708818e6aSHeiko Stuebner .clk_size = ARRAY_SIZE(rk3399_isp_clks),
52808818e6aSHeiko Stuebner .isrs = rk3399_isp_isrs,
52908818e6aSHeiko Stuebner .isr_size = ARRAY_SIZE(rk3399_isp_isrs),
530fc672d80SHeiko Stuebner .isp_ver = RKISP1_V10,
53182754080SPaul Elder .features = RKISP1_FEATURE_MIPI_CSI2
5322db6cad8SPaul Elder | RKISP1_FEATURE_SELF_PATH
5332db6cad8SPaul Elder | RKISP1_FEATURE_DUAL_CROP,
534e6938cc1SHelen Koike };
535e6938cc1SHelen Koike
536*9f9cd26aSPaul Elder static const char * const imx8mp_isp_clks[] = {
537*9f9cd26aSPaul Elder "isp",
538*9f9cd26aSPaul Elder "hclk",
539*9f9cd26aSPaul Elder "aclk",
540*9f9cd26aSPaul Elder };
541*9f9cd26aSPaul Elder
542*9f9cd26aSPaul Elder static const struct rkisp1_isr_data imx8mp_isp_isrs[] = {
543*9f9cd26aSPaul Elder { NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) },
544*9f9cd26aSPaul Elder };
545*9f9cd26aSPaul Elder
546*9f9cd26aSPaul Elder static const struct rkisp1_info imx8mp_isp_info = {
547*9f9cd26aSPaul Elder .clks = imx8mp_isp_clks,
548*9f9cd26aSPaul Elder .clk_size = ARRAY_SIZE(imx8mp_isp_clks),
549*9f9cd26aSPaul Elder .isrs = imx8mp_isp_isrs,
550*9f9cd26aSPaul Elder .isr_size = ARRAY_SIZE(imx8mp_isp_isrs),
551*9f9cd26aSPaul Elder .isp_ver = RKISP1_V_IMX8MP,
552*9f9cd26aSPaul Elder .features = RKISP1_FEATURE_MAIN_STRIDE
553*9f9cd26aSPaul Elder | RKISP1_FEATURE_DMA_34BIT,
554e6938cc1SHelen Koike };
555e6938cc1SHelen Koike
556e6938cc1SHelen Koike static const struct of_device_id rkisp1_of_match[] = {
557e6938cc1SHelen Koike {
558ecf8d36fSHeiko Stuebner .compatible = "rockchip,px30-cif-isp",
559cdce5b95SLaurent Pinchart .data = &px30_isp_info,
560ecf8d36fSHeiko Stuebner },
561ecf8d36fSHeiko Stuebner {
562e6938cc1SHelen Koike .compatible = "rockchip,rk3399-cif-isp",
563cdce5b95SLaurent Pinchart .data = &rk3399_isp_info,
564e6938cc1SHelen Koike },
565*9f9cd26aSPaul Elder {
566*9f9cd26aSPaul Elder .compatible = "fsl,imx8mp-isp",
567*9f9cd26aSPaul Elder .data = &imx8mp_isp_info,
568*9f9cd26aSPaul Elder },
569e6938cc1SHelen Koike {},
570e6938cc1SHelen Koike };
571e6938cc1SHelen Koike MODULE_DEVICE_TABLE(of, rkisp1_of_match);
572e6938cc1SHelen Koike
rkisp1_probe(struct platform_device * pdev)573e6938cc1SHelen Koike static int rkisp1_probe(struct platform_device *pdev)
574e6938cc1SHelen Koike {
575cdce5b95SLaurent Pinchart const struct rkisp1_info *info;
576e6938cc1SHelen Koike struct device *dev = &pdev->dev;
577e6938cc1SHelen Koike struct rkisp1_device *rkisp1;
578e6938cc1SHelen Koike struct v4l2_device *v4l2_dev;
579e6938cc1SHelen Koike unsigned int i;
580da1484c7SPaul Elder u64 dma_mask;
581e6938cc1SHelen Koike int ret, irq;
582196179c5SLaurent Pinchart u32 cif_id;
583e6938cc1SHelen Koike
584e6938cc1SHelen Koike rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL);
585e6938cc1SHelen Koike if (!rkisp1)
586e6938cc1SHelen Koike return -ENOMEM;
587e6938cc1SHelen Koike
5889125aee7SPaul Elder info = of_device_get_match_data(dev);
5899125aee7SPaul Elder rkisp1->info = info;
5909125aee7SPaul Elder
591e6938cc1SHelen Koike dev_set_drvdata(dev, rkisp1);
592e6938cc1SHelen Koike rkisp1->dev = dev;
593e6938cc1SHelen Koike
594da1484c7SPaul Elder dma_mask = rkisp1_has_feature(rkisp1, DMA_34BIT) ? DMA_BIT_MASK(34) :
595da1484c7SPaul Elder DMA_BIT_MASK(32);
596da1484c7SPaul Elder
597da1484c7SPaul Elder ret = dma_set_mask_and_coherent(dev, dma_mask);
598da1484c7SPaul Elder if (ret)
599da1484c7SPaul Elder return ret;
600da1484c7SPaul Elder
601e6938cc1SHelen Koike mutex_init(&rkisp1->stream_lock);
602e6938cc1SHelen Koike
603e6938cc1SHelen Koike rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0);
604e6938cc1SHelen Koike if (IS_ERR(rkisp1->base_addr))
605e6938cc1SHelen Koike return PTR_ERR(rkisp1->base_addr);
606e6938cc1SHelen Koike
60707538746STomi Valkeinen for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il)
60807538746STomi Valkeinen rkisp1->irqs[il] = -1;
60907538746STomi Valkeinen
610cdce5b95SLaurent Pinchart for (i = 0; i < info->isr_size; i++) {
611cdce5b95SLaurent Pinchart irq = info->isrs[i].name
612cdce5b95SLaurent Pinchart ? platform_get_irq_byname(pdev, info->isrs[i].name)
613fd83ef8fSLaurent Pinchart : platform_get_irq(pdev, i);
614e6938cc1SHelen Koike if (irq < 0)
615e6938cc1SHelen Koike return irq;
616e6938cc1SHelen Koike
61707538746STomi Valkeinen for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) {
61807538746STomi Valkeinen if (info->isrs[i].line_mask & BIT(il))
61907538746STomi Valkeinen rkisp1->irqs[il] = irq;
62007538746STomi Valkeinen }
62107538746STomi Valkeinen
622a107d643STomi Valkeinen ret = devm_request_irq(dev, irq, info->isrs[i].isr, IRQF_SHARED,
623e6938cc1SHelen Koike dev_driver_string(dev), dev);
624e6938cc1SHelen Koike if (ret) {
625e6938cc1SHelen Koike dev_err(dev, "request irq failed: %d\n", ret);
626e6938cc1SHelen Koike return ret;
627e6938cc1SHelen Koike }
62808818e6aSHeiko Stuebner }
629e6938cc1SHelen Koike
630cdce5b95SLaurent Pinchart for (i = 0; i < info->clk_size; i++)
631cdce5b95SLaurent Pinchart rkisp1->clks[i].id = info->clks[i];
632cdce5b95SLaurent Pinchart ret = devm_clk_bulk_get(dev, info->clk_size, rkisp1->clks);
633e6938cc1SHelen Koike if (ret)
634e6938cc1SHelen Koike return ret;
635cdce5b95SLaurent Pinchart rkisp1->clk_size = info->clk_size;
636e6938cc1SHelen Koike
637fdac4ce9SLaurent Pinchart if (info->isp_ver == RKISP1_V_IMX8MP) {
638fdac4ce9SLaurent Pinchart unsigned int id;
639fdac4ce9SLaurent Pinchart
640fdac4ce9SLaurent Pinchart rkisp1->gasket = syscon_regmap_lookup_by_phandle_args(dev->of_node,
641fdac4ce9SLaurent Pinchart "fsl,blk-ctrl",
642fdac4ce9SLaurent Pinchart 1, &id);
643fdac4ce9SLaurent Pinchart if (IS_ERR(rkisp1->gasket)) {
644fdac4ce9SLaurent Pinchart ret = PTR_ERR(rkisp1->gasket);
645fdac4ce9SLaurent Pinchart dev_err(dev, "failed to get gasket: %d\n", ret);
646fdac4ce9SLaurent Pinchart return ret;
647fdac4ce9SLaurent Pinchart }
648fdac4ce9SLaurent Pinchart
649fdac4ce9SLaurent Pinchart rkisp1->gasket_id = id;
650fdac4ce9SLaurent Pinchart }
651fdac4ce9SLaurent Pinchart
652e6938cc1SHelen Koike pm_runtime_enable(&pdev->dev);
653e6938cc1SHelen Koike
654196179c5SLaurent Pinchart ret = pm_runtime_resume_and_get(&pdev->dev);
655196179c5SLaurent Pinchart if (ret)
656196179c5SLaurent Pinchart goto err_pm_runtime_disable;
657196179c5SLaurent Pinchart
658196179c5SLaurent Pinchart cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID);
659196179c5SLaurent Pinchart dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id);
660196179c5SLaurent Pinchart
661196179c5SLaurent Pinchart pm_runtime_put(&pdev->dev);
662196179c5SLaurent Pinchart
663cdce5b95SLaurent Pinchart rkisp1->media_dev.hw_revision = info->isp_ver;
664e6938cc1SHelen Koike strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME,
665e6938cc1SHelen Koike sizeof(rkisp1->media_dev.model));
666e6938cc1SHelen Koike rkisp1->media_dev.dev = &pdev->dev;
667e6938cc1SHelen Koike strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO,
668e6938cc1SHelen Koike sizeof(rkisp1->media_dev.bus_info));
669e6938cc1SHelen Koike media_device_init(&rkisp1->media_dev);
670e6938cc1SHelen Koike
671e6938cc1SHelen Koike v4l2_dev = &rkisp1->v4l2_dev;
672e6938cc1SHelen Koike v4l2_dev->mdev = &rkisp1->media_dev;
673e6938cc1SHelen Koike strscpy(v4l2_dev->name, RKISP1_DRIVER_NAME, sizeof(v4l2_dev->name));
674e6938cc1SHelen Koike
675e6938cc1SHelen Koike ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev);
676e6938cc1SHelen Koike if (ret)
677452f604aSTomi Valkeinen goto err_media_dev_cleanup;
678e6938cc1SHelen Koike
679e6938cc1SHelen Koike ret = media_device_register(&rkisp1->media_dev);
680e6938cc1SHelen Koike if (ret) {
681e6938cc1SHelen Koike dev_err(dev, "Failed to register media device: %d\n", ret);
682e6938cc1SHelen Koike goto err_unreg_v4l2_dev;
683e6938cc1SHelen Koike }
684e6938cc1SHelen Koike
6857d4f126fSLaurent Pinchart if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) {
6868082e2f4SPaul Elder ret = rkisp1_csi_init(rkisp1);
687e6938cc1SHelen Koike if (ret)
688e6938cc1SHelen Koike goto err_unreg_media_dev;
6897d4f126fSLaurent Pinchart }
690e6938cc1SHelen Koike
6918082e2f4SPaul Elder ret = rkisp1_entities_register(rkisp1);
6928082e2f4SPaul Elder if (ret)
6938082e2f4SPaul Elder goto err_cleanup_csi;
6948082e2f4SPaul Elder
69598bfd0cdSLaurent Pinchart ret = rkisp1_subdev_notifier_register(rkisp1);
69698bfd0cdSLaurent Pinchart if (ret)
69798bfd0cdSLaurent Pinchart goto err_unreg_entities;
69898bfd0cdSLaurent Pinchart
699e6938cc1SHelen Koike rkisp1_debug_init(rkisp1);
700e6938cc1SHelen Koike
701e6938cc1SHelen Koike return 0;
702e6938cc1SHelen Koike
70398bfd0cdSLaurent Pinchart err_unreg_entities:
70498bfd0cdSLaurent Pinchart rkisp1_entities_unregister(rkisp1);
7058082e2f4SPaul Elder err_cleanup_csi:
706900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
7078082e2f4SPaul Elder rkisp1_csi_cleanup(rkisp1);
708e6938cc1SHelen Koike err_unreg_media_dev:
709e6938cc1SHelen Koike media_device_unregister(&rkisp1->media_dev);
710e6938cc1SHelen Koike err_unreg_v4l2_dev:
711e6938cc1SHelen Koike v4l2_device_unregister(&rkisp1->v4l2_dev);
712452f604aSTomi Valkeinen err_media_dev_cleanup:
713452f604aSTomi Valkeinen media_device_cleanup(&rkisp1->media_dev);
71413c98102SLaurent Pinchart err_pm_runtime_disable:
715e6938cc1SHelen Koike pm_runtime_disable(&pdev->dev);
716e6938cc1SHelen Koike return ret;
717e6938cc1SHelen Koike }
718e6938cc1SHelen Koike
rkisp1_remove(struct platform_device * pdev)719073dcc08SUwe Kleine-König static void rkisp1_remove(struct platform_device *pdev)
720e6938cc1SHelen Koike {
721e6938cc1SHelen Koike struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev);
722e6938cc1SHelen Koike
7233c8c1539SSakari Ailus v4l2_async_nf_unregister(&rkisp1->notifier);
7243c8c1539SSakari Ailus v4l2_async_nf_cleanup(&rkisp1->notifier);
725e6938cc1SHelen Koike
7266ff02276SLaurent Pinchart rkisp1_entities_unregister(rkisp1);
727900f6676SLaurent Pinchart if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
7288082e2f4SPaul Elder rkisp1_csi_cleanup(rkisp1);
7298682037dSLaurent Pinchart rkisp1_debug_cleanup(rkisp1);
730e6938cc1SHelen Koike
731e6938cc1SHelen Koike media_device_unregister(&rkisp1->media_dev);
732e6938cc1SHelen Koike v4l2_device_unregister(&rkisp1->v4l2_dev);
733e6938cc1SHelen Koike
734452f604aSTomi Valkeinen media_device_cleanup(&rkisp1->media_dev);
735452f604aSTomi Valkeinen
736e6938cc1SHelen Koike pm_runtime_disable(&pdev->dev);
737e6938cc1SHelen Koike }
738e6938cc1SHelen Koike
739e6938cc1SHelen Koike static struct platform_driver rkisp1_drv = {
740e6938cc1SHelen Koike .driver = {
741e6938cc1SHelen Koike .name = RKISP1_DRIVER_NAME,
742e6938cc1SHelen Koike .of_match_table = of_match_ptr(rkisp1_of_match),
743e6938cc1SHelen Koike .pm = &rkisp1_pm_ops,
744e6938cc1SHelen Koike },
745e6938cc1SHelen Koike .probe = rkisp1_probe,
746073dcc08SUwe Kleine-König .remove_new = rkisp1_remove,
747e6938cc1SHelen Koike };
748e6938cc1SHelen Koike
749e6938cc1SHelen Koike module_platform_driver(rkisp1_drv);
750e6938cc1SHelen Koike MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
751e6938cc1SHelen Koike MODULE_LICENSE("Dual MIT/GPL");
752