xref: /linux/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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", &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