xref: /linux/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c (revision 900f6676760d56c4bd091ac66494141977a429a9)
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>
13e6938cc1SHelen Koike #include <linux/module.h>
14e6938cc1SHelen Koike #include <linux/of.h>
15e6938cc1SHelen Koike #include <linux/of_graph.h>
167c7e33b7SRob Herring #include <linux/platform_device.h>
17e6938cc1SHelen Koike #include <linux/pinctrl/consumer.h>
188082e2f4SPaul Elder #include <linux/pm_runtime.h>
19e6938cc1SHelen Koike #include <media/v4l2-fwnode.h>
2098bfd0cdSLaurent Pinchart #include <media/v4l2-mc.h>
21e6938cc1SHelen Koike 
22e6938cc1SHelen Koike #include "rkisp1-common.h"
238082e2f4SPaul Elder #include "rkisp1-csi.h"
24e6938cc1SHelen Koike 
25e6938cc1SHelen Koike /*
26e6938cc1SHelen Koike  * ISP Details
27e6938cc1SHelen Koike  * -----------
28e6938cc1SHelen Koike  *
29e6938cc1SHelen Koike  * ISP Comprises with:
30e6938cc1SHelen Koike  *	MIPI serial camera interface
31e6938cc1SHelen Koike  *	Image Signal Processing
32e6938cc1SHelen Koike  *	Many Image Enhancement Blocks
33e6938cc1SHelen Koike  *	Crop
34e6938cc1SHelen Koike  *	Resizer
35e6938cc1SHelen Koike  *	RBG display ready image
36e6938cc1SHelen Koike  *	Image Rotation
37e6938cc1SHelen Koike  *
38e6938cc1SHelen Koike  * ISP Block Diagram
39e6938cc1SHelen Koike  * -----------------
40e6938cc1SHelen Koike  *                                                             rkisp1-resizer.c          rkisp1-capture.c
41e6938cc1SHelen Koike  *                                                          |====================|  |=======================|
42e6938cc1SHelen Koike  *                                rkisp1-isp.c                              Main Picture Path
43e6938cc1SHelen Koike  *                        |==========================|      |===============================================|
44e6938cc1SHelen Koike  *                        +-----------+  +--+--+--+--+      +--------+  +--------+              +-----------+
45e6938cc1SHelen Koike  *                        |           |  |  |  |  |  |      |        |  |        |              |           |
46e6938cc1SHelen Koike  * +--------+    |\       |           |  |  |  |  |  |   -->|  Crop  |->|  RSZ   |------------->|           |
47e6938cc1SHelen Koike  * |  MIPI  |--->|  \     |           |  |  |  |  |  |   |  |        |  |        |              |           |
48e6938cc1SHelen Koike  * +--------+    |   |    |           |  |IE|IE|IE|IE|   |  +--------+  +--------+              |  Memory   |
49e6938cc1SHelen Koike  *               |MUX|--->|    ISP    |->|0 |1 |2 |3 |---+                                      | Interface |
50e6938cc1SHelen Koike  * +--------+    |   |    |           |  |  |  |  |  |   |  +--------+  +--------+  +--------+  |           |
51e6938cc1SHelen Koike  * |Parallel|--->|  /     |           |  |  |  |  |  |   |  |        |  |        |  |        |  |           |
52e6938cc1SHelen Koike  * +--------+    |/       |           |  |  |  |  |  |   -->|  Crop  |->|  RSZ   |->|  RGB   |->|           |
53e6938cc1SHelen Koike  *                        |           |  |  |  |  |  |      |        |  |        |  | Rotate |  |           |
54e6938cc1SHelen Koike  *                        +-----------+  +--+--+--+--+      +--------+  +--------+  +--------+  +-----------+
55e6938cc1SHelen Koike  *                                               ^
56e6938cc1SHelen Koike  * +--------+                                    |          |===============================================|
57e6938cc1SHelen Koike  * |  DMA   |------------------------------------+                          Self Picture Path
58e6938cc1SHelen Koike  * +--------+
59e6938cc1SHelen Koike  *
60e6938cc1SHelen Koike  *         rkisp1-stats.c        rkisp1-params.c
61e6938cc1SHelen Koike  *       |===============|      |===============|
62e6938cc1SHelen Koike  *       +---------------+      +---------------+
63e6938cc1SHelen Koike  *       |               |      |               |
64e6938cc1SHelen Koike  *       |      ISP      |      |      ISP      |
65e6938cc1SHelen Koike  *       |               |      |               |
66e6938cc1SHelen Koike  *       +---------------+      +---------------+
67e6938cc1SHelen Koike  *
68e6938cc1SHelen Koike  *
69e6938cc1SHelen Koike  * Media Topology
70e6938cc1SHelen Koike  * --------------
7198bfd0cdSLaurent Pinchart  *
72e6938cc1SHelen Koike  *          +----------+       +----------+
7398bfd0cdSLaurent Pinchart  *          | Sensor 1 |       | Sensor X |
74e6938cc1SHelen Koike  *          ------------  ...  ------------
75e6938cc1SHelen Koike  *          |    0     |       |    0     |
7698bfd0cdSLaurent Pinchart  *          +----------+       +----------+
7798bfd0cdSLaurent Pinchart  *               |                  |
7898bfd0cdSLaurent Pinchart  *                \----\       /----/
7998bfd0cdSLaurent Pinchart  *                     |       |
8098bfd0cdSLaurent Pinchart  *                     v       v
8198bfd0cdSLaurent Pinchart  *                  +-------------+
8298bfd0cdSLaurent Pinchart  *                  |      0      |
8398bfd0cdSLaurent Pinchart  *                  ---------------
8498bfd0cdSLaurent Pinchart  *                  |  CSI-2 RX   |
8598bfd0cdSLaurent Pinchart  *                  ---------------         +-----------+
8698bfd0cdSLaurent Pinchart  *                  |      1      |         |  params   |
8798bfd0cdSLaurent Pinchart  *                  +-------------+         | (output)  |
8898bfd0cdSLaurent Pinchart  *                         |               +-----------+
8998bfd0cdSLaurent Pinchart  *                         v                     |
9098bfd0cdSLaurent Pinchart  *                      +------+------+          |
9198bfd0cdSLaurent Pinchart  *                      |  0   |  1   |<---------+
9298bfd0cdSLaurent Pinchart  *                      |------+------|
93e6938cc1SHelen Koike  *                      |     ISP     |
94e6938cc1SHelen Koike  *                      |------+------|
95e6938cc1SHelen Koike  *        +-------------|  2   |  3   |----------+
96e6938cc1SHelen Koike  *        |             +------+------+          |
97e6938cc1SHelen Koike  *        |                |                     |
98e6938cc1SHelen Koike  *        v                v                     v
99e6938cc1SHelen Koike  *  +- ---------+    +-----------+         +-----------+
100e6938cc1SHelen Koike  *  |     0     |    |     0     |         |   stats   |
101e6938cc1SHelen Koike  *  -------------    -------------         | (capture) |
102e6938cc1SHelen Koike  *  |  Resizer  |    |  Resizer  |         +-----------+
103e6938cc1SHelen Koike  *  ------------|    ------------|
104e6938cc1SHelen Koike  *  |     1     |    |     1     |
105e6938cc1SHelen Koike  *  +-----------+    +-----------+
106e6938cc1SHelen Koike  *        |                |
107e6938cc1SHelen Koike  *        v                v
108e6938cc1SHelen Koike  *  +-----------+    +-----------+
109e6938cc1SHelen Koike  *  | selfpath  |    | mainpath  |
110e6938cc1SHelen Koike  *  | (capture) |    | (capture) |
111e6938cc1SHelen Koike  *  +-----------+    +-----------+
112e6938cc1SHelen Koike  */
113e6938cc1SHelen Koike 
11408818e6aSHeiko Stuebner struct rkisp1_isr_data {
11508818e6aSHeiko Stuebner 	const char *name;
11608818e6aSHeiko Stuebner 	irqreturn_t (*isr)(int irq, void *ctx);
11707538746STomi Valkeinen 	u32 line_mask;
11808818e6aSHeiko Stuebner };
11908818e6aSHeiko Stuebner 
120e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
121e6938cc1SHelen Koike  * Sensor DT bindings
122e6938cc1SHelen Koike  */
123e6938cc1SHelen Koike 
124e6938cc1SHelen Koike static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
125e6938cc1SHelen Koike 					struct v4l2_subdev *sd,
126adb2dcd5SSakari Ailus 					struct v4l2_async_connection *asc)
127e6938cc1SHelen Koike {
128e6938cc1SHelen Koike 	struct rkisp1_device *rkisp1 =
129e6938cc1SHelen Koike 		container_of(notifier, struct rkisp1_device, notifier);
130e6938cc1SHelen Koike 	struct rkisp1_sensor_async *s_asd =
131adb2dcd5SSakari Ailus 		container_of(asc, struct rkisp1_sensor_async, asd);
132bc374e17SLaurent Pinchart 	int source_pad;
133f42f4558SPaul Elder 	int ret;
134e6938cc1SHelen Koike 
135e6938cc1SHelen Koike 	s_asd->sd = sd;
136e6938cc1SHelen Koike 
137b0b8ab68SLaurent Pinchart 	source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep,
138bc374e17SLaurent Pinchart 						 MEDIA_PAD_FL_SOURCE);
139bc374e17SLaurent Pinchart 	if (source_pad < 0) {
140bc374e17SLaurent Pinchart 		dev_err(rkisp1->dev, "failed to find source pad for %s\n",
141bc374e17SLaurent Pinchart 			sd->name);
142bc374e17SLaurent Pinchart 		return source_pad;
143bc374e17SLaurent Pinchart 	}
144bc374e17SLaurent Pinchart 
145f42f4558SPaul Elder 	if (s_asd->port == 0)
14698bfd0cdSLaurent Pinchart 		return rkisp1_csi_link_sensor(rkisp1, sd, s_asd, source_pad);
147f42f4558SPaul Elder 
148f42f4558SPaul Elder 	ret = media_create_pad_link(&sd->entity, source_pad,
149f42f4558SPaul Elder 				    &rkisp1->isp.sd.entity,
150f42f4558SPaul Elder 				    RKISP1_ISP_PAD_SINK_VIDEO,
151f42f4558SPaul Elder 				    !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0);
152f42f4558SPaul Elder 	if (ret) {
153f42f4558SPaul Elder 		dev_err(rkisp1->dev, "failed to link source pad of %s\n",
154f42f4558SPaul Elder 			sd->name);
155f42f4558SPaul Elder 		return ret;
156f42f4558SPaul Elder 	}
157f42f4558SPaul Elder 
158f42f4558SPaul Elder 	return 0;
159e6938cc1SHelen Koike }
160e6938cc1SHelen Koike 
161e6938cc1SHelen Koike static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
162e6938cc1SHelen Koike {
163e6938cc1SHelen Koike 	struct rkisp1_device *rkisp1 =
164e6938cc1SHelen Koike 		container_of(notifier, struct rkisp1_device, notifier);
165e6938cc1SHelen Koike 
166cf7a8e24SLaurent Pinchart 	return v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev);
167e6938cc1SHelen Koike }
168e6938cc1SHelen Koike 
169adb2dcd5SSakari Ailus static void rkisp1_subdev_notifier_destroy(struct v4l2_async_connection *asc)
170b0b8ab68SLaurent Pinchart {
171b0b8ab68SLaurent Pinchart 	struct rkisp1_sensor_async *rk_asd =
172adb2dcd5SSakari Ailus 		container_of(asc, struct rkisp1_sensor_async, asd);
173b0b8ab68SLaurent Pinchart 
174b0b8ab68SLaurent Pinchart 	fwnode_handle_put(rk_asd->source_ep);
175b0b8ab68SLaurent Pinchart }
176b0b8ab68SLaurent Pinchart 
177e6938cc1SHelen Koike static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
178e6938cc1SHelen Koike 	.bound = rkisp1_subdev_notifier_bound,
179e6938cc1SHelen Koike 	.complete = rkisp1_subdev_notifier_complete,
180b0b8ab68SLaurent Pinchart 	.destroy = rkisp1_subdev_notifier_destroy,
181e6938cc1SHelen Koike };
182e6938cc1SHelen Koike 
183124b89f8SLaurent Pinchart static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
184e6938cc1SHelen Koike {
185e6938cc1SHelen Koike 	struct v4l2_async_notifier *ntf = &rkisp1->notifier;
1862452171eSPaul Elder 	struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev);
1872452171eSPaul Elder 	struct fwnode_handle *ep;
188bc374e17SLaurent Pinchart 	unsigned int index = 0;
1892452171eSPaul Elder 	int ret = 0;
190e6938cc1SHelen Koike 
191b8ec754aSSakari Ailus 	v4l2_async_nf_init(ntf, &rkisp1->v4l2_dev);
192e6938cc1SHelen Koike 
1932452171eSPaul Elder 	ntf->ops = &rkisp1_subdev_notifier_ops;
1942452171eSPaul Elder 
1952452171eSPaul Elder 	fwnode_graph_for_each_endpoint(fwnode, ep) {
196f42f4558SPaul Elder 		struct fwnode_handle *port;
197f42f4558SPaul Elder 		struct v4l2_fwnode_endpoint vep = { };
198b01edcbdSLaurent Pinchart 		struct rkisp1_sensor_async *rk_asd;
1992452171eSPaul Elder 		struct fwnode_handle *source;
200f42f4558SPaul Elder 		u32 reg = 0;
201e6938cc1SHelen Koike 
202f42f4558SPaul Elder 		/* Select the bus type based on the port. */
203f42f4558SPaul Elder 		port = fwnode_get_parent(ep);
204f42f4558SPaul Elder 		fwnode_property_read_u32(port, "reg", &reg);
205f42f4558SPaul Elder 		fwnode_handle_put(port);
206f42f4558SPaul Elder 
207f42f4558SPaul Elder 		switch (reg) {
208f42f4558SPaul Elder 		case 0:
2097d4f126fSLaurent Pinchart 			/* MIPI CSI-2 port */
210*900f6676SLaurent Pinchart 			if (!rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
2117d4f126fSLaurent Pinchart 				dev_err(rkisp1->dev,
2127d4f126fSLaurent Pinchart 					"internal CSI must be available for port 0\n");
2137d4f126fSLaurent Pinchart 				ret = -EINVAL;
2147d4f126fSLaurent Pinchart 				break;
2157d4f126fSLaurent Pinchart 			}
2167d4f126fSLaurent Pinchart 
217f42f4558SPaul Elder 			vep.bus_type = V4L2_MBUS_CSI2_DPHY;
218f42f4558SPaul Elder 			break;
219f42f4558SPaul Elder 
220f42f4558SPaul Elder 		case 1:
221f42f4558SPaul Elder 			/*
222f42f4558SPaul Elder 			 * Parallel port. The bus-type property in DT is
223f42f4558SPaul Elder 			 * mandatory for port 1, it will be used to determine if
224f42f4558SPaul Elder 			 * it's PARALLEL or BT656.
225f42f4558SPaul Elder 			 */
226f42f4558SPaul Elder 			vep.bus_type = V4L2_MBUS_UNKNOWN;
227f42f4558SPaul Elder 			break;
228f42f4558SPaul Elder 		}
229f42f4558SPaul Elder 
230f42f4558SPaul Elder 		/* Parse the endpoint and validate the bus type. */
231e6938cc1SHelen Koike 		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
2322452171eSPaul Elder 		if (ret) {
2332452171eSPaul Elder 			dev_err(rkisp1->dev, "failed to parse endpoint %pfw\n",
2342452171eSPaul Elder 				ep);
2352452171eSPaul Elder 			break;
2362452171eSPaul Elder 		}
237e6938cc1SHelen Koike 
238f42f4558SPaul Elder 		if (vep.base.port == 1) {
239f42f4558SPaul Elder 			if (vep.bus_type != V4L2_MBUS_PARALLEL &&
240f42f4558SPaul Elder 			    vep.bus_type != V4L2_MBUS_BT656) {
241f42f4558SPaul Elder 				dev_err(rkisp1->dev,
242f42f4558SPaul Elder 					"port 1 must be parallel or BT656\n");
243f42f4558SPaul Elder 				ret = -EINVAL;
244f42f4558SPaul Elder 				break;
245f42f4558SPaul Elder 			}
246f42f4558SPaul Elder 		}
247f42f4558SPaul Elder 
248f42f4558SPaul Elder 		/* Add the async subdev to the notifier. */
249b0b8ab68SLaurent Pinchart 		source = fwnode_graph_get_remote_endpoint(ep);
250b0b8ab68SLaurent Pinchart 		if (!source) {
251b0b8ab68SLaurent Pinchart 			dev_err(rkisp1->dev,
252b0b8ab68SLaurent Pinchart 				"endpoint %pfw has no remote endpoint\n",
253b0b8ab68SLaurent Pinchart 				ep);
254b0b8ab68SLaurent Pinchart 			ret = -ENODEV;
2552452171eSPaul Elder 			break;
256b0b8ab68SLaurent Pinchart 		}
257b0b8ab68SLaurent Pinchart 
258b0b8ab68SLaurent Pinchart 		rk_asd = v4l2_async_nf_add_fwnode(ntf, source,
259b0b8ab68SLaurent Pinchart 						  struct rkisp1_sensor_async);
260b01edcbdSLaurent Pinchart 		if (IS_ERR(rk_asd)) {
2612452171eSPaul Elder 			fwnode_handle_put(source);
262b01edcbdSLaurent Pinchart 			ret = PTR_ERR(rk_asd);
2632452171eSPaul Elder 			break;
264e6938cc1SHelen Koike 		}
265e6938cc1SHelen Koike 
266bc374e17SLaurent Pinchart 		rk_asd->index = index++;
267b0b8ab68SLaurent Pinchart 		rk_asd->source_ep = source;
268e6938cc1SHelen Koike 		rk_asd->mbus_type = vep.bus_type;
269f42f4558SPaul Elder 		rk_asd->port = vep.base.port;
270f42f4558SPaul Elder 
271f42f4558SPaul Elder 		if (vep.bus_type == V4L2_MBUS_CSI2_DPHY) {
272e6938cc1SHelen Koike 			rk_asd->mbus_flags = vep.bus.mipi_csi2.flags;
273e6938cc1SHelen Koike 			rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes;
274f42f4558SPaul Elder 		} else {
275f42f4558SPaul Elder 			rk_asd->mbus_flags = vep.bus.parallel.flags;
276f42f4558SPaul Elder 		}
277e6938cc1SHelen Koike 
278f42f4558SPaul Elder 		dev_dbg(rkisp1->dev, "registered ep id %d, bus type %u, %u lanes\n",
279f42f4558SPaul Elder 			vep.base.id, rk_asd->mbus_type, rk_asd->lanes);
2802452171eSPaul Elder 	}
281e6938cc1SHelen Koike 
2822452171eSPaul Elder 	if (ret) {
283e6938cc1SHelen Koike 		fwnode_handle_put(ep);
2843c8c1539SSakari Ailus 		v4l2_async_nf_cleanup(ntf);
285e6938cc1SHelen Koike 		return ret;
286e6938cc1SHelen Koike 	}
287e6938cc1SHelen Koike 
2882452171eSPaul Elder 	if (!index)
289e6938cc1SHelen Koike 		dev_dbg(rkisp1->dev, "no remote subdevice found\n");
2902452171eSPaul Elder 
291b8ec754aSSakari Ailus 	ret = v4l2_async_nf_register(ntf);
292e6938cc1SHelen Koike 	if (ret) {
2933c8c1539SSakari Ailus 		v4l2_async_nf_cleanup(ntf);
294e6938cc1SHelen Koike 		return ret;
295e6938cc1SHelen Koike 	}
2962452171eSPaul Elder 
297e6938cc1SHelen Koike 	return 0;
298e6938cc1SHelen Koike }
299e6938cc1SHelen Koike 
300e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
301e6938cc1SHelen Koike  * Power
302e6938cc1SHelen Koike  */
303e6938cc1SHelen Koike 
304e6938cc1SHelen Koike static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
305e6938cc1SHelen Koike {
306e6938cc1SHelen Koike 	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
307e6938cc1SHelen Koike 
308e6938cc1SHelen Koike 	clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks);
309e6938cc1SHelen Koike 	return pinctrl_pm_select_sleep_state(dev);
310e6938cc1SHelen Koike }
311e6938cc1SHelen Koike 
312e6938cc1SHelen Koike static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
313e6938cc1SHelen Koike {
314e6938cc1SHelen Koike 	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
315e6938cc1SHelen Koike 	int ret;
316e6938cc1SHelen Koike 
317e6938cc1SHelen Koike 	ret = pinctrl_pm_select_default_state(dev);
318e6938cc1SHelen Koike 	if (ret)
319e6938cc1SHelen Koike 		return ret;
320e6938cc1SHelen Koike 	ret = clk_bulk_prepare_enable(rkisp1->clk_size, rkisp1->clks);
321e6938cc1SHelen Koike 	if (ret)
322e6938cc1SHelen Koike 		return ret;
323e6938cc1SHelen Koike 
324e6938cc1SHelen Koike 	return 0;
325e6938cc1SHelen Koike }
326e6938cc1SHelen Koike 
327e6938cc1SHelen Koike static const struct dev_pm_ops rkisp1_pm_ops = {
328e6938cc1SHelen Koike 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
329e6938cc1SHelen Koike 				pm_runtime_force_resume)
330e6938cc1SHelen Koike 	SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL)
331e6938cc1SHelen Koike };
332e6938cc1SHelen Koike 
333e6938cc1SHelen Koike /* ----------------------------------------------------------------------------
334e6938cc1SHelen Koike  * Core
335e6938cc1SHelen Koike  */
336e6938cc1SHelen Koike 
337cf7a8e24SLaurent Pinchart static int rkisp1_create_links(struct rkisp1_device *rkisp1)
338cf7a8e24SLaurent Pinchart {
339cf7a8e24SLaurent Pinchart 	unsigned int i;
340cf7a8e24SLaurent Pinchart 	int ret;
341cf7a8e24SLaurent Pinchart 
342*900f6676SLaurent Pinchart 	if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
34398bfd0cdSLaurent Pinchart 		/* Link the CSI receiver to the ISP. */
3447d4f126fSLaurent Pinchart 		ret = media_create_pad_link(&rkisp1->csi.sd.entity,
3457d4f126fSLaurent Pinchart 					    RKISP1_CSI_PAD_SRC,
34698bfd0cdSLaurent Pinchart 					    &rkisp1->isp.sd.entity,
34798bfd0cdSLaurent Pinchart 					    RKISP1_ISP_PAD_SINK_VIDEO,
34898bfd0cdSLaurent Pinchart 					    MEDIA_LNK_FL_ENABLED);
34998bfd0cdSLaurent Pinchart 		if (ret)
35098bfd0cdSLaurent Pinchart 			return ret;
3517d4f126fSLaurent Pinchart 	}
35298bfd0cdSLaurent Pinchart 
353cf7a8e24SLaurent Pinchart 	/* create ISP->RSZ->CAP links */
354cf7a8e24SLaurent Pinchart 	for (i = 0; i < 2; i++) {
355cf7a8e24SLaurent Pinchart 		struct media_entity *resizer =
356cf7a8e24SLaurent Pinchart 			&rkisp1->resizer_devs[i].sd.entity;
357cf7a8e24SLaurent Pinchart 		struct media_entity *capture =
358cf7a8e24SLaurent Pinchart 			&rkisp1->capture_devs[i].vnode.vdev.entity;
359cf7a8e24SLaurent Pinchart 
360cf7a8e24SLaurent Pinchart 		ret = media_create_pad_link(&rkisp1->isp.sd.entity,
361cf7a8e24SLaurent Pinchart 					    RKISP1_ISP_PAD_SOURCE_VIDEO,
362cf7a8e24SLaurent Pinchart 					    resizer, RKISP1_RSZ_PAD_SINK,
363cf7a8e24SLaurent Pinchart 					    MEDIA_LNK_FL_ENABLED);
364cf7a8e24SLaurent Pinchart 		if (ret)
365cf7a8e24SLaurent Pinchart 			return ret;
366cf7a8e24SLaurent Pinchart 
367cf7a8e24SLaurent Pinchart 		ret = media_create_pad_link(resizer, RKISP1_RSZ_PAD_SRC,
368cf7a8e24SLaurent Pinchart 					    capture, 0,
369cf7a8e24SLaurent Pinchart 					    MEDIA_LNK_FL_ENABLED |
370cf7a8e24SLaurent Pinchart 					    MEDIA_LNK_FL_IMMUTABLE);
371cf7a8e24SLaurent Pinchart 		if (ret)
372cf7a8e24SLaurent Pinchart 			return ret;
373cf7a8e24SLaurent Pinchart 	}
374cf7a8e24SLaurent Pinchart 
375cf7a8e24SLaurent Pinchart 	/* params links */
376cf7a8e24SLaurent Pinchart 	ret = media_create_pad_link(&rkisp1->params.vnode.vdev.entity, 0,
377cf7a8e24SLaurent Pinchart 				    &rkisp1->isp.sd.entity,
378cf7a8e24SLaurent Pinchart 				    RKISP1_ISP_PAD_SINK_PARAMS,
379cf7a8e24SLaurent Pinchart 				    MEDIA_LNK_FL_ENABLED |
380cf7a8e24SLaurent Pinchart 				    MEDIA_LNK_FL_IMMUTABLE);
381cf7a8e24SLaurent Pinchart 	if (ret)
382cf7a8e24SLaurent Pinchart 		return ret;
383cf7a8e24SLaurent Pinchart 
384cf7a8e24SLaurent Pinchart 	/* 3A stats links */
385cf7a8e24SLaurent Pinchart 	return media_create_pad_link(&rkisp1->isp.sd.entity,
386cf7a8e24SLaurent Pinchart 				     RKISP1_ISP_PAD_SOURCE_STATS,
387cf7a8e24SLaurent Pinchart 				     &rkisp1->stats.vnode.vdev.entity, 0,
388cf7a8e24SLaurent Pinchart 				     MEDIA_LNK_FL_ENABLED |
389cf7a8e24SLaurent Pinchart 				     MEDIA_LNK_FL_IMMUTABLE);
390cf7a8e24SLaurent Pinchart }
391cf7a8e24SLaurent Pinchart 
3926ff02276SLaurent Pinchart static void rkisp1_entities_unregister(struct rkisp1_device *rkisp1)
3936ff02276SLaurent Pinchart {
394*900f6676SLaurent Pinchart 	if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
395b298f059SPaul Elder 		rkisp1_csi_unregister(rkisp1);
3966ff02276SLaurent Pinchart 	rkisp1_params_unregister(rkisp1);
3976ff02276SLaurent Pinchart 	rkisp1_stats_unregister(rkisp1);
3986ff02276SLaurent Pinchart 	rkisp1_capture_devs_unregister(rkisp1);
3996ff02276SLaurent Pinchart 	rkisp1_resizer_devs_unregister(rkisp1);
4006ff02276SLaurent Pinchart 	rkisp1_isp_unregister(rkisp1);
4016ff02276SLaurent Pinchart }
4026ff02276SLaurent Pinchart 
403e6938cc1SHelen Koike static int rkisp1_entities_register(struct rkisp1_device *rkisp1)
404e6938cc1SHelen Koike {
405e6938cc1SHelen Koike 	int ret;
406e6938cc1SHelen Koike 
407e6938cc1SHelen Koike 	ret = rkisp1_isp_register(rkisp1);
408e6938cc1SHelen Koike 	if (ret)
4096ff02276SLaurent Pinchart 		goto error;
410e6938cc1SHelen Koike 
411e6938cc1SHelen Koike 	ret = rkisp1_resizer_devs_register(rkisp1);
412e6938cc1SHelen Koike 	if (ret)
4136ff02276SLaurent Pinchart 		goto error;
414e6938cc1SHelen Koike 
415e6938cc1SHelen Koike 	ret = rkisp1_capture_devs_register(rkisp1);
416e6938cc1SHelen Koike 	if (ret)
4176ff02276SLaurent Pinchart 		goto error;
418e6938cc1SHelen Koike 
419e6938cc1SHelen Koike 	ret = rkisp1_stats_register(rkisp1);
420e6938cc1SHelen Koike 	if (ret)
4216ff02276SLaurent Pinchart 		goto error;
422e6938cc1SHelen Koike 
423e6938cc1SHelen Koike 	ret = rkisp1_params_register(rkisp1);
424e6938cc1SHelen Koike 	if (ret)
4256ff02276SLaurent Pinchart 		goto error;
426e6938cc1SHelen Koike 
427*900f6676SLaurent Pinchart 	if (rkisp1_has_feature(rkisp1, MIPI_CSI2)) {
428b298f059SPaul Elder 		ret = rkisp1_csi_register(rkisp1);
429b298f059SPaul Elder 		if (ret)
430b298f059SPaul Elder 			goto error;
4317d4f126fSLaurent Pinchart 	}
432b298f059SPaul Elder 
433cf7a8e24SLaurent Pinchart 	ret = rkisp1_create_links(rkisp1);
434cf7a8e24SLaurent Pinchart 	if (ret)
435cf7a8e24SLaurent Pinchart 		goto error;
436cf7a8e24SLaurent Pinchart 
437e6938cc1SHelen Koike 	return 0;
4386ff02276SLaurent Pinchart 
4396ff02276SLaurent Pinchart error:
4406ff02276SLaurent Pinchart 	rkisp1_entities_unregister(rkisp1);
441e6938cc1SHelen Koike 	return ret;
442e6938cc1SHelen Koike }
443e6938cc1SHelen Koike 
444e6938cc1SHelen Koike static irqreturn_t rkisp1_isr(int irq, void *ctx)
445e6938cc1SHelen Koike {
4463eb7910eSTomi Valkeinen 	irqreturn_t ret = IRQ_NONE;
4473eb7910eSTomi Valkeinen 
448e6938cc1SHelen Koike 	/*
449e6938cc1SHelen Koike 	 * Call rkisp1_capture_isr() first to handle the frame that
450e6938cc1SHelen Koike 	 * potentially completed using the current frame_sequence number before
451e6938cc1SHelen Koike 	 * it is potentially incremented by rkisp1_isp_isr() in the vertical
452e6938cc1SHelen Koike 	 * sync.
453e6938cc1SHelen Koike 	 */
454e6938cc1SHelen Koike 
4553eb7910eSTomi Valkeinen 	if (rkisp1_capture_isr(irq, ctx) == IRQ_HANDLED)
4563eb7910eSTomi Valkeinen 		ret = IRQ_HANDLED;
4573eb7910eSTomi Valkeinen 
4583eb7910eSTomi Valkeinen 	if (rkisp1_isp_isr(irq, ctx) == IRQ_HANDLED)
4593eb7910eSTomi Valkeinen 		ret = IRQ_HANDLED;
4603eb7910eSTomi Valkeinen 
4613eb7910eSTomi Valkeinen 	if (rkisp1_csi_isr(irq, ctx) == IRQ_HANDLED)
4623eb7910eSTomi Valkeinen 		ret = IRQ_HANDLED;
4633eb7910eSTomi Valkeinen 
4643eb7910eSTomi Valkeinen 	return ret;
465e6938cc1SHelen Koike }
466e6938cc1SHelen Koike 
467ecf8d36fSHeiko Stuebner static const char * const px30_isp_clks[] = {
468ecf8d36fSHeiko Stuebner 	"isp",
469ecf8d36fSHeiko Stuebner 	"aclk",
470ecf8d36fSHeiko Stuebner 	"hclk",
471ecf8d36fSHeiko Stuebner 	"pclk",
472ecf8d36fSHeiko Stuebner };
473ecf8d36fSHeiko Stuebner 
474ecf8d36fSHeiko Stuebner static const struct rkisp1_isr_data px30_isp_isrs[] = {
47507538746STomi Valkeinen 	{ "isp", rkisp1_isp_isr, BIT(RKISP1_IRQ_ISP) },
47607538746STomi Valkeinen 	{ "mi", rkisp1_capture_isr, BIT(RKISP1_IRQ_MI) },
47707538746STomi Valkeinen 	{ "mipi", rkisp1_csi_isr, BIT(RKISP1_IRQ_MIPI) },
478ecf8d36fSHeiko Stuebner };
479ecf8d36fSHeiko Stuebner 
480cdce5b95SLaurent Pinchart static const struct rkisp1_info px30_isp_info = {
481ecf8d36fSHeiko Stuebner 	.clks = px30_isp_clks,
482ecf8d36fSHeiko Stuebner 	.clk_size = ARRAY_SIZE(px30_isp_clks),
483ecf8d36fSHeiko Stuebner 	.isrs = px30_isp_isrs,
484ecf8d36fSHeiko Stuebner 	.isr_size = ARRAY_SIZE(px30_isp_isrs),
485ecf8d36fSHeiko Stuebner 	.isp_ver = RKISP1_V12,
486f1b8f171SLaurent Pinchart 	.features = RKISP1_FEATURE_MIPI_CSI2,
487ecf8d36fSHeiko Stuebner };
488ecf8d36fSHeiko Stuebner 
489e6938cc1SHelen Koike static const char * const rk3399_isp_clks[] = {
490e6938cc1SHelen Koike 	"isp",
491e6938cc1SHelen Koike 	"aclk",
492e6938cc1SHelen Koike 	"hclk",
493e6938cc1SHelen Koike };
494e6938cc1SHelen Koike 
49508818e6aSHeiko Stuebner static const struct rkisp1_isr_data rk3399_isp_isrs[] = {
49607538746STomi Valkeinen 	{ NULL, rkisp1_isr, BIT(RKISP1_IRQ_ISP) | BIT(RKISP1_IRQ_MI) | BIT(RKISP1_IRQ_MIPI) },
49708818e6aSHeiko Stuebner };
49808818e6aSHeiko Stuebner 
499cdce5b95SLaurent Pinchart static const struct rkisp1_info rk3399_isp_info = {
500e6938cc1SHelen Koike 	.clks = rk3399_isp_clks,
50108818e6aSHeiko Stuebner 	.clk_size = ARRAY_SIZE(rk3399_isp_clks),
50208818e6aSHeiko Stuebner 	.isrs = rk3399_isp_isrs,
50308818e6aSHeiko Stuebner 	.isr_size = ARRAY_SIZE(rk3399_isp_isrs),
504fc672d80SHeiko Stuebner 	.isp_ver = RKISP1_V10,
505f1b8f171SLaurent Pinchart 	.features = RKISP1_FEATURE_MIPI_CSI2,
506e6938cc1SHelen Koike };
507e6938cc1SHelen Koike 
508e6938cc1SHelen Koike static const struct of_device_id rkisp1_of_match[] = {
509e6938cc1SHelen Koike 	{
510ecf8d36fSHeiko Stuebner 		.compatible = "rockchip,px30-cif-isp",
511cdce5b95SLaurent Pinchart 		.data = &px30_isp_info,
512ecf8d36fSHeiko Stuebner 	},
513ecf8d36fSHeiko Stuebner 	{
514e6938cc1SHelen Koike 		.compatible = "rockchip,rk3399-cif-isp",
515cdce5b95SLaurent Pinchart 		.data = &rk3399_isp_info,
516e6938cc1SHelen Koike 	},
517e6938cc1SHelen Koike 	{},
518e6938cc1SHelen Koike };
519e6938cc1SHelen Koike MODULE_DEVICE_TABLE(of, rkisp1_of_match);
520e6938cc1SHelen Koike 
521e6938cc1SHelen Koike static int rkisp1_probe(struct platform_device *pdev)
522e6938cc1SHelen Koike {
523cdce5b95SLaurent Pinchart 	const struct rkisp1_info *info;
524e6938cc1SHelen Koike 	struct device *dev = &pdev->dev;
525e6938cc1SHelen Koike 	struct rkisp1_device *rkisp1;
526e6938cc1SHelen Koike 	struct v4l2_device *v4l2_dev;
527e6938cc1SHelen Koike 	unsigned int i;
528e6938cc1SHelen Koike 	int ret, irq;
529196179c5SLaurent Pinchart 	u32 cif_id;
530e6938cc1SHelen Koike 
531e6938cc1SHelen Koike 	rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL);
532e6938cc1SHelen Koike 	if (!rkisp1)
533e6938cc1SHelen Koike 		return -ENOMEM;
534e6938cc1SHelen Koike 
5359125aee7SPaul Elder 	info = of_device_get_match_data(dev);
5369125aee7SPaul Elder 	rkisp1->info = info;
5379125aee7SPaul Elder 
538e6938cc1SHelen Koike 	dev_set_drvdata(dev, rkisp1);
539e6938cc1SHelen Koike 	rkisp1->dev = dev;
540e6938cc1SHelen Koike 
541e6938cc1SHelen Koike 	mutex_init(&rkisp1->stream_lock);
542e6938cc1SHelen Koike 
543e6938cc1SHelen Koike 	rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0);
544e6938cc1SHelen Koike 	if (IS_ERR(rkisp1->base_addr))
545e6938cc1SHelen Koike 		return PTR_ERR(rkisp1->base_addr);
546e6938cc1SHelen Koike 
54707538746STomi Valkeinen 	for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il)
54807538746STomi Valkeinen 		rkisp1->irqs[il] = -1;
54907538746STomi Valkeinen 
550cdce5b95SLaurent Pinchart 	for (i = 0; i < info->isr_size; i++) {
551cdce5b95SLaurent Pinchart 		irq = info->isrs[i].name
552cdce5b95SLaurent Pinchart 		    ? platform_get_irq_byname(pdev, info->isrs[i].name)
553fd83ef8fSLaurent Pinchart 		    : platform_get_irq(pdev, i);
554e6938cc1SHelen Koike 		if (irq < 0)
555e6938cc1SHelen Koike 			return irq;
556e6938cc1SHelen Koike 
55707538746STomi Valkeinen 		for (unsigned int il = 0; il < ARRAY_SIZE(rkisp1->irqs); ++il) {
55807538746STomi Valkeinen 			if (info->isrs[i].line_mask & BIT(il))
55907538746STomi Valkeinen 				rkisp1->irqs[il] = irq;
56007538746STomi Valkeinen 		}
56107538746STomi Valkeinen 
56285d2a31fSTomi Valkeinen 		ret = devm_request_irq(dev, irq, info->isrs[i].isr, 0,
563e6938cc1SHelen Koike 				       dev_driver_string(dev), dev);
564e6938cc1SHelen Koike 		if (ret) {
565e6938cc1SHelen Koike 			dev_err(dev, "request irq failed: %d\n", ret);
566e6938cc1SHelen Koike 			return ret;
567e6938cc1SHelen Koike 		}
56808818e6aSHeiko Stuebner 	}
569e6938cc1SHelen Koike 
570cdce5b95SLaurent Pinchart 	for (i = 0; i < info->clk_size; i++)
571cdce5b95SLaurent Pinchart 		rkisp1->clks[i].id = info->clks[i];
572cdce5b95SLaurent Pinchart 	ret = devm_clk_bulk_get(dev, info->clk_size, rkisp1->clks);
573e6938cc1SHelen Koike 	if (ret)
574e6938cc1SHelen Koike 		return ret;
575cdce5b95SLaurent Pinchart 	rkisp1->clk_size = info->clk_size;
576e6938cc1SHelen Koike 
577e6938cc1SHelen Koike 	pm_runtime_enable(&pdev->dev);
578e6938cc1SHelen Koike 
579196179c5SLaurent Pinchart 	ret = pm_runtime_resume_and_get(&pdev->dev);
580196179c5SLaurent Pinchart 	if (ret)
581196179c5SLaurent Pinchart 		goto err_pm_runtime_disable;
582196179c5SLaurent Pinchart 
583196179c5SLaurent Pinchart 	cif_id = rkisp1_read(rkisp1, RKISP1_CIF_VI_ID);
584196179c5SLaurent Pinchart 	dev_dbg(rkisp1->dev, "CIF_ID 0x%08x\n", cif_id);
585196179c5SLaurent Pinchart 
586196179c5SLaurent Pinchart 	pm_runtime_put(&pdev->dev);
587196179c5SLaurent Pinchart 
588cdce5b95SLaurent Pinchart 	rkisp1->media_dev.hw_revision = info->isp_ver;
589e6938cc1SHelen Koike 	strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME,
590e6938cc1SHelen Koike 		sizeof(rkisp1->media_dev.model));
591e6938cc1SHelen Koike 	rkisp1->media_dev.dev = &pdev->dev;
592e6938cc1SHelen Koike 	strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO,
593e6938cc1SHelen Koike 		sizeof(rkisp1->media_dev.bus_info));
594e6938cc1SHelen Koike 	media_device_init(&rkisp1->media_dev);
595e6938cc1SHelen Koike 
596e6938cc1SHelen Koike 	v4l2_dev = &rkisp1->v4l2_dev;
597e6938cc1SHelen Koike 	v4l2_dev->mdev = &rkisp1->media_dev;
598e6938cc1SHelen Koike 	strscpy(v4l2_dev->name, RKISP1_DRIVER_NAME, sizeof(v4l2_dev->name));
599e6938cc1SHelen Koike 
600e6938cc1SHelen Koike 	ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev);
601e6938cc1SHelen Koike 	if (ret)
602452f604aSTomi Valkeinen 		goto err_media_dev_cleanup;
603e6938cc1SHelen Koike 
604e6938cc1SHelen Koike 	ret = media_device_register(&rkisp1->media_dev);
605e6938cc1SHelen Koike 	if (ret) {
606e6938cc1SHelen Koike 		dev_err(dev, "Failed to register media device: %d\n", ret);
607e6938cc1SHelen Koike 		goto err_unreg_v4l2_dev;
608e6938cc1SHelen Koike 	}
609e6938cc1SHelen Koike 
6107d4f126fSLaurent Pinchart 	if (rkisp1->info->features & RKISP1_FEATURE_MIPI_CSI2) {
6118082e2f4SPaul Elder 		ret = rkisp1_csi_init(rkisp1);
612e6938cc1SHelen Koike 		if (ret)
613e6938cc1SHelen Koike 			goto err_unreg_media_dev;
6147d4f126fSLaurent Pinchart 	}
615e6938cc1SHelen Koike 
6168082e2f4SPaul Elder 	ret = rkisp1_entities_register(rkisp1);
6178082e2f4SPaul Elder 	if (ret)
6188082e2f4SPaul Elder 		goto err_cleanup_csi;
6198082e2f4SPaul Elder 
62098bfd0cdSLaurent Pinchart 	ret = rkisp1_subdev_notifier_register(rkisp1);
62198bfd0cdSLaurent Pinchart 	if (ret)
62298bfd0cdSLaurent Pinchart 		goto err_unreg_entities;
62398bfd0cdSLaurent Pinchart 
624e6938cc1SHelen Koike 	rkisp1_debug_init(rkisp1);
625e6938cc1SHelen Koike 
626e6938cc1SHelen Koike 	return 0;
627e6938cc1SHelen Koike 
62898bfd0cdSLaurent Pinchart err_unreg_entities:
62998bfd0cdSLaurent Pinchart 	rkisp1_entities_unregister(rkisp1);
6308082e2f4SPaul Elder err_cleanup_csi:
631*900f6676SLaurent Pinchart 	if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
6328082e2f4SPaul Elder 		rkisp1_csi_cleanup(rkisp1);
633e6938cc1SHelen Koike err_unreg_media_dev:
634e6938cc1SHelen Koike 	media_device_unregister(&rkisp1->media_dev);
635e6938cc1SHelen Koike err_unreg_v4l2_dev:
636e6938cc1SHelen Koike 	v4l2_device_unregister(&rkisp1->v4l2_dev);
637452f604aSTomi Valkeinen err_media_dev_cleanup:
638452f604aSTomi Valkeinen 	media_device_cleanup(&rkisp1->media_dev);
63913c98102SLaurent Pinchart err_pm_runtime_disable:
640e6938cc1SHelen Koike 	pm_runtime_disable(&pdev->dev);
641e6938cc1SHelen Koike 	return ret;
642e6938cc1SHelen Koike }
643e6938cc1SHelen Koike 
644073dcc08SUwe Kleine-König static void rkisp1_remove(struct platform_device *pdev)
645e6938cc1SHelen Koike {
646e6938cc1SHelen Koike 	struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev);
647e6938cc1SHelen Koike 
6483c8c1539SSakari Ailus 	v4l2_async_nf_unregister(&rkisp1->notifier);
6493c8c1539SSakari Ailus 	v4l2_async_nf_cleanup(&rkisp1->notifier);
650e6938cc1SHelen Koike 
6516ff02276SLaurent Pinchart 	rkisp1_entities_unregister(rkisp1);
652*900f6676SLaurent Pinchart 	if (rkisp1_has_feature(rkisp1, MIPI_CSI2))
6538082e2f4SPaul Elder 		rkisp1_csi_cleanup(rkisp1);
6548682037dSLaurent Pinchart 	rkisp1_debug_cleanup(rkisp1);
655e6938cc1SHelen Koike 
656e6938cc1SHelen Koike 	media_device_unregister(&rkisp1->media_dev);
657e6938cc1SHelen Koike 	v4l2_device_unregister(&rkisp1->v4l2_dev);
658e6938cc1SHelen Koike 
659452f604aSTomi Valkeinen 	media_device_cleanup(&rkisp1->media_dev);
660452f604aSTomi Valkeinen 
661e6938cc1SHelen Koike 	pm_runtime_disable(&pdev->dev);
662e6938cc1SHelen Koike }
663e6938cc1SHelen Koike 
664e6938cc1SHelen Koike static struct platform_driver rkisp1_drv = {
665e6938cc1SHelen Koike 	.driver = {
666e6938cc1SHelen Koike 		.name = RKISP1_DRIVER_NAME,
667e6938cc1SHelen Koike 		.of_match_table = of_match_ptr(rkisp1_of_match),
668e6938cc1SHelen Koike 		.pm = &rkisp1_pm_ops,
669e6938cc1SHelen Koike 	},
670e6938cc1SHelen Koike 	.probe = rkisp1_probe,
671073dcc08SUwe Kleine-König 	.remove_new = rkisp1_remove,
672e6938cc1SHelen Koike };
673e6938cc1SHelen Koike 
674e6938cc1SHelen Koike module_platform_driver(rkisp1_drv);
675e6938cc1SHelen Koike MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
676e6938cc1SHelen Koike MODULE_LICENSE("Dual MIT/GPL");
677