xref: /linux/drivers/media/platform/synopsys/dw-mipi-csi2rx.c (revision b7ef56a07672e0d7ebe71c9d9b45f959f0c2f8e8)
1*355a1100SMichael Riesch // SPDX-License-Identifier: GPL-2.0
2*355a1100SMichael Riesch /*
3*355a1100SMichael Riesch  * Synopsys DesignWare MIPI CSI-2 Receiver Driver
4*355a1100SMichael Riesch  *
5*355a1100SMichael Riesch  * Copyright (C) 2019 Rockchip Electronics Co., Ltd.
6*355a1100SMichael Riesch  * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
7*355a1100SMichael Riesch  * Copyright (C) 2026 Collabora, Ltd.
8*355a1100SMichael Riesch  */
9*355a1100SMichael Riesch 
10*355a1100SMichael Riesch #include <linux/clk.h>
11*355a1100SMichael Riesch #include <linux/delay.h>
12*355a1100SMichael Riesch #include <linux/io.h>
13*355a1100SMichael Riesch #include <linux/module.h>
14*355a1100SMichael Riesch #include <linux/of.h>
15*355a1100SMichael Riesch #include <linux/phy/phy.h>
16*355a1100SMichael Riesch #include <linux/platform_device.h>
17*355a1100SMichael Riesch #include <linux/pm_runtime.h>
18*355a1100SMichael Riesch #include <linux/property.h>
19*355a1100SMichael Riesch #include <linux/reset.h>
20*355a1100SMichael Riesch 
21*355a1100SMichael Riesch #include <media/mipi-csi2.h>
22*355a1100SMichael Riesch #include <media/v4l2-ctrls.h>
23*355a1100SMichael Riesch #include <media/v4l2-fwnode.h>
24*355a1100SMichael Riesch #include <media/v4l2-mc.h>
25*355a1100SMichael Riesch #include <media/v4l2-subdev.h>
26*355a1100SMichael Riesch 
27*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_N_LANES		0x04
28*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_RESETN		0x10
29*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_PHY_STATE	0x14
30*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_ERR1		0x20
31*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_ERR2		0x24
32*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_MSK1		0x28
33*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_MSK2		0x2c
34*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_CONTROL		0x40
35*355a1100SMichael Riesch 
36*355a1100SMichael Riesch #define SW_CPHY_EN(x)		((x) << 0)
37*355a1100SMichael Riesch #define SW_DSI_EN(x)		((x) << 4)
38*355a1100SMichael Riesch #define SW_DATATYPE_FS(x)	((x) << 8)
39*355a1100SMichael Riesch #define SW_DATATYPE_FE(x)	((x) << 14)
40*355a1100SMichael Riesch #define SW_DATATYPE_LS(x)	((x) << 20)
41*355a1100SMichael Riesch #define SW_DATATYPE_LE(x)	((x) << 26)
42*355a1100SMichael Riesch 
43*355a1100SMichael Riesch #define DW_MIPI_CSI2RX_CLKS_MAX	1
44*355a1100SMichael Riesch 
45*355a1100SMichael Riesch enum {
46*355a1100SMichael Riesch 	DW_MIPI_CSI2RX_PAD_SINK,
47*355a1100SMichael Riesch 	DW_MIPI_CSI2RX_PAD_SRC,
48*355a1100SMichael Riesch 	DW_MIPI_CSI2RX_PAD_MAX,
49*355a1100SMichael Riesch };
50*355a1100SMichael Riesch 
51*355a1100SMichael Riesch struct dw_mipi_csi2rx_format {
52*355a1100SMichael Riesch 	u32 code;
53*355a1100SMichael Riesch 	u8 depth;
54*355a1100SMichael Riesch 	u8 csi_dt;
55*355a1100SMichael Riesch };
56*355a1100SMichael Riesch 
57*355a1100SMichael Riesch struct dw_mipi_csi2rx_device {
58*355a1100SMichael Riesch 	struct device *dev;
59*355a1100SMichael Riesch 
60*355a1100SMichael Riesch 	void __iomem *base_addr;
61*355a1100SMichael Riesch 	struct clk_bulk_data *clks;
62*355a1100SMichael Riesch 	unsigned int clks_num;
63*355a1100SMichael Riesch 	struct phy *phy;
64*355a1100SMichael Riesch 	struct reset_control *reset;
65*355a1100SMichael Riesch 
66*355a1100SMichael Riesch 	const struct dw_mipi_csi2rx_format *formats;
67*355a1100SMichael Riesch 	unsigned int formats_num;
68*355a1100SMichael Riesch 
69*355a1100SMichael Riesch 	struct media_pad pads[DW_MIPI_CSI2RX_PAD_MAX];
70*355a1100SMichael Riesch 	struct v4l2_async_notifier notifier;
71*355a1100SMichael Riesch 	struct v4l2_subdev sd;
72*355a1100SMichael Riesch 
73*355a1100SMichael Riesch 	enum v4l2_mbus_type bus_type;
74*355a1100SMichael Riesch 	u32 lanes_num;
75*355a1100SMichael Riesch };
76*355a1100SMichael Riesch 
77*355a1100SMichael Riesch static const struct v4l2_mbus_framefmt default_format = {
78*355a1100SMichael Riesch 	.width = 3840,
79*355a1100SMichael Riesch 	.height = 2160,
80*355a1100SMichael Riesch 	.code = MEDIA_BUS_FMT_SRGGB10_1X10,
81*355a1100SMichael Riesch 	.field = V4L2_FIELD_NONE,
82*355a1100SMichael Riesch 	.colorspace = V4L2_COLORSPACE_RAW,
83*355a1100SMichael Riesch 	.ycbcr_enc = V4L2_YCBCR_ENC_601,
84*355a1100SMichael Riesch 	.quantization = V4L2_QUANTIZATION_FULL_RANGE,
85*355a1100SMichael Riesch 	.xfer_func = V4L2_XFER_FUNC_NONE,
86*355a1100SMichael Riesch };
87*355a1100SMichael Riesch 
88*355a1100SMichael Riesch static const struct dw_mipi_csi2rx_format formats[] = {
89*355a1100SMichael Riesch 	/* YUV formats */
90*355a1100SMichael Riesch 	{
91*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_YUYV8_1X16,
92*355a1100SMichael Riesch 		.depth = 16,
93*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_YUV422_8B,
94*355a1100SMichael Riesch 	},
95*355a1100SMichael Riesch 	{
96*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_UYVY8_1X16,
97*355a1100SMichael Riesch 		.depth = 16,
98*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_YUV422_8B,
99*355a1100SMichael Riesch 	},
100*355a1100SMichael Riesch 	{
101*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_YVYU8_1X16,
102*355a1100SMichael Riesch 		.depth = 16,
103*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_YUV422_8B,
104*355a1100SMichael Riesch 	},
105*355a1100SMichael Riesch 	{
106*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_VYUY8_1X16,
107*355a1100SMichael Riesch 		.depth = 16,
108*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_YUV422_8B,
109*355a1100SMichael Riesch 	},
110*355a1100SMichael Riesch 	/* RGB formats */
111*355a1100SMichael Riesch 	{
112*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_RGB888_1X24,
113*355a1100SMichael Riesch 		.depth = 24,
114*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RGB888,
115*355a1100SMichael Riesch 	},
116*355a1100SMichael Riesch 	{
117*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_BGR888_1X24,
118*355a1100SMichael Riesch 		.depth = 24,
119*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RGB888,
120*355a1100SMichael Riesch 	},
121*355a1100SMichael Riesch 	/* Bayer formats */
122*355a1100SMichael Riesch 	{
123*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
124*355a1100SMichael Riesch 		.depth = 8,
125*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW8,
126*355a1100SMichael Riesch 	},
127*355a1100SMichael Riesch 	{
128*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
129*355a1100SMichael Riesch 		.depth = 8,
130*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW8,
131*355a1100SMichael Riesch 	},
132*355a1100SMichael Riesch 	{
133*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
134*355a1100SMichael Riesch 		.depth = 8,
135*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW8,
136*355a1100SMichael Riesch 	},
137*355a1100SMichael Riesch 	{
138*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
139*355a1100SMichael Riesch 		.depth = 8,
140*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW8,
141*355a1100SMichael Riesch 	},
142*355a1100SMichael Riesch 	{
143*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
144*355a1100SMichael Riesch 		.depth = 10,
145*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW10,
146*355a1100SMichael Riesch 	},
147*355a1100SMichael Riesch 	{
148*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
149*355a1100SMichael Riesch 		.depth = 10,
150*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW10,
151*355a1100SMichael Riesch 	},
152*355a1100SMichael Riesch 	{
153*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
154*355a1100SMichael Riesch 		.depth = 10,
155*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW10,
156*355a1100SMichael Riesch 	},
157*355a1100SMichael Riesch 	{
158*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
159*355a1100SMichael Riesch 		.depth = 10,
160*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW10,
161*355a1100SMichael Riesch 	},
162*355a1100SMichael Riesch 	{
163*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
164*355a1100SMichael Riesch 		.depth = 12,
165*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW12,
166*355a1100SMichael Riesch 	},
167*355a1100SMichael Riesch 	{
168*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
169*355a1100SMichael Riesch 		.depth = 12,
170*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW12,
171*355a1100SMichael Riesch 	},
172*355a1100SMichael Riesch 	{
173*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
174*355a1100SMichael Riesch 		.depth = 12,
175*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW12,
176*355a1100SMichael Riesch 	},
177*355a1100SMichael Riesch 	{
178*355a1100SMichael Riesch 		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
179*355a1100SMichael Riesch 		.depth = 12,
180*355a1100SMichael Riesch 		.csi_dt = MIPI_CSI2_DT_RAW12,
181*355a1100SMichael Riesch 	},
182*355a1100SMichael Riesch };
183*355a1100SMichael Riesch 
184*355a1100SMichael Riesch static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
185*355a1100SMichael Riesch {
186*355a1100SMichael Riesch 	return container_of(sd, struct dw_mipi_csi2rx_device, sd);
187*355a1100SMichael Riesch }
188*355a1100SMichael Riesch 
189*355a1100SMichael Riesch static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2,
190*355a1100SMichael Riesch 					unsigned int addr, u32 val)
191*355a1100SMichael Riesch {
192*355a1100SMichael Riesch 	writel(val, csi2->base_addr + addr);
193*355a1100SMichael Riesch }
194*355a1100SMichael Riesch 
195*355a1100SMichael Riesch static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2,
196*355a1100SMichael Riesch 				      unsigned int addr)
197*355a1100SMichael Riesch {
198*355a1100SMichael Riesch 	return readl(csi2->base_addr + addr);
199*355a1100SMichael Riesch }
200*355a1100SMichael Riesch 
201*355a1100SMichael Riesch static const struct dw_mipi_csi2rx_format *
202*355a1100SMichael Riesch dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code)
203*355a1100SMichael Riesch {
204*355a1100SMichael Riesch 	WARN_ON(csi2->formats_num == 0);
205*355a1100SMichael Riesch 
206*355a1100SMichael Riesch 	for (unsigned int i = 0; i < csi2->formats_num; i++) {
207*355a1100SMichael Riesch 		const struct dw_mipi_csi2rx_format *format = &csi2->formats[i];
208*355a1100SMichael Riesch 
209*355a1100SMichael Riesch 		if (format->code == mbus_code)
210*355a1100SMichael Riesch 			return format;
211*355a1100SMichael Riesch 	}
212*355a1100SMichael Riesch 
213*355a1100SMichael Riesch 	return NULL;
214*355a1100SMichael Riesch }
215*355a1100SMichael Riesch 
216*355a1100SMichael Riesch static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2)
217*355a1100SMichael Riesch {
218*355a1100SMichael Riesch 	struct media_pad *source_pad;
219*355a1100SMichael Riesch 	union phy_configure_opts opts;
220*355a1100SMichael Riesch 	u32 lanes = csi2->lanes_num;
221*355a1100SMichael Riesch 	u32 control = 0;
222*355a1100SMichael Riesch 	s64 link_freq;
223*355a1100SMichael Riesch 	int ret;
224*355a1100SMichael Riesch 
225*355a1100SMichael Riesch 	if (lanes < 1 || lanes > 4)
226*355a1100SMichael Riesch 		return -EINVAL;
227*355a1100SMichael Riesch 
228*355a1100SMichael Riesch 	source_pad = media_pad_remote_pad_unique(
229*355a1100SMichael Riesch 		&csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]);
230*355a1100SMichael Riesch 	if (IS_ERR(source_pad))
231*355a1100SMichael Riesch 		return PTR_ERR(source_pad);
232*355a1100SMichael Riesch 
233*355a1100SMichael Riesch 	/* set mult and div to 0, thus completely rely on V4L2_CID_LINK_FREQ */
234*355a1100SMichael Riesch 	link_freq = v4l2_get_link_freq(source_pad, 0, 0);
235*355a1100SMichael Riesch 	if (link_freq < 0)
236*355a1100SMichael Riesch 		return link_freq;
237*355a1100SMichael Riesch 
238*355a1100SMichael Riesch 	switch (csi2->bus_type) {
239*355a1100SMichael Riesch 	case V4L2_MBUS_CSI2_DPHY:
240*355a1100SMichael Riesch 		ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2,
241*355a1100SMichael Riesch 								 lanes, &opts.mipi_dphy);
242*355a1100SMichael Riesch 		if (ret)
243*355a1100SMichael Riesch 			return ret;
244*355a1100SMichael Riesch 
245*355a1100SMichael Riesch 		ret = phy_set_mode(csi2->phy, PHY_MODE_MIPI_DPHY);
246*355a1100SMichael Riesch 		if (ret)
247*355a1100SMichael Riesch 			return ret;
248*355a1100SMichael Riesch 
249*355a1100SMichael Riesch 		ret = phy_configure(csi2->phy, &opts);
250*355a1100SMichael Riesch 		if (ret)
251*355a1100SMichael Riesch 			return ret;
252*355a1100SMichael Riesch 
253*355a1100SMichael Riesch 		control |= SW_CPHY_EN(0);
254*355a1100SMichael Riesch 		break;
255*355a1100SMichael Riesch 
256*355a1100SMichael Riesch 	case V4L2_MBUS_CSI2_CPHY:
257*355a1100SMichael Riesch 		/* TODO: implement CPHY configuration */
258*355a1100SMichael Riesch 		return -EOPNOTSUPP;
259*355a1100SMichael Riesch 	default:
260*355a1100SMichael Riesch 		return -EINVAL;
261*355a1100SMichael Riesch 	}
262*355a1100SMichael Riesch 
263*355a1100SMichael Riesch 	control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) |
264*355a1100SMichael Riesch 		   SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03);
265*355a1100SMichael Riesch 
266*355a1100SMichael Riesch 	dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1);
267*355a1100SMichael Riesch 	dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control);
268*355a1100SMichael Riesch 	dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1);
269*355a1100SMichael Riesch 
270*355a1100SMichael Riesch 	return phy_power_on(csi2->phy);
271*355a1100SMichael Riesch }
272*355a1100SMichael Riesch 
273*355a1100SMichael Riesch static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2)
274*355a1100SMichael Riesch {
275*355a1100SMichael Riesch 	phy_power_off(csi2->phy);
276*355a1100SMichael Riesch 
277*355a1100SMichael Riesch 	dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0);
278*355a1100SMichael Riesch 	dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0);
279*355a1100SMichael Riesch 	dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0);
280*355a1100SMichael Riesch }
281*355a1100SMichael Riesch 
282*355a1100SMichael Riesch static const struct media_entity_operations dw_mipi_csi2rx_media_ops = {
283*355a1100SMichael Riesch 	.link_validate = v4l2_subdev_link_validate,
284*355a1100SMichael Riesch };
285*355a1100SMichael Riesch 
286*355a1100SMichael Riesch static int
287*355a1100SMichael Riesch dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd,
288*355a1100SMichael Riesch 			      struct v4l2_subdev_state *sd_state,
289*355a1100SMichael Riesch 			      struct v4l2_subdev_mbus_code_enum *code)
290*355a1100SMichael Riesch {
291*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
292*355a1100SMichael Riesch 
293*355a1100SMichael Riesch 	switch (code->pad) {
294*355a1100SMichael Riesch 	case DW_MIPI_CSI2RX_PAD_SRC:
295*355a1100SMichael Riesch 		if (code->index)
296*355a1100SMichael Riesch 			return -EINVAL;
297*355a1100SMichael Riesch 
298*355a1100SMichael Riesch 		code->code =
299*355a1100SMichael Riesch 			v4l2_subdev_state_get_format(sd_state,
300*355a1100SMichael Riesch 						     DW_MIPI_CSI2RX_PAD_SINK)->code;
301*355a1100SMichael Riesch 
302*355a1100SMichael Riesch 		return 0;
303*355a1100SMichael Riesch 	case DW_MIPI_CSI2RX_PAD_SINK:
304*355a1100SMichael Riesch 		if (code->index > csi2->formats_num)
305*355a1100SMichael Riesch 			return -EINVAL;
306*355a1100SMichael Riesch 
307*355a1100SMichael Riesch 		code->code = csi2->formats[code->index].code;
308*355a1100SMichael Riesch 		return 0;
309*355a1100SMichael Riesch 	default:
310*355a1100SMichael Riesch 		return -EINVAL;
311*355a1100SMichael Riesch 	}
312*355a1100SMichael Riesch }
313*355a1100SMichael Riesch 
314*355a1100SMichael Riesch static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd,
315*355a1100SMichael Riesch 				  struct v4l2_subdev_state *state,
316*355a1100SMichael Riesch 				  struct v4l2_subdev_format *format)
317*355a1100SMichael Riesch {
318*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
319*355a1100SMichael Riesch 	const struct dw_mipi_csi2rx_format *fmt;
320*355a1100SMichael Riesch 	struct v4l2_mbus_framefmt *sink, *src;
321*355a1100SMichael Riesch 
322*355a1100SMichael Riesch 	/* the format on the source pad always matches the sink pad */
323*355a1100SMichael Riesch 	if (format->pad == DW_MIPI_CSI2RX_PAD_SRC)
324*355a1100SMichael Riesch 		return v4l2_subdev_get_fmt(sd, state, format);
325*355a1100SMichael Riesch 
326*355a1100SMichael Riesch 	sink = v4l2_subdev_state_get_format(state, format->pad, format->stream);
327*355a1100SMichael Riesch 	if (!sink)
328*355a1100SMichael Riesch 		return -EINVAL;
329*355a1100SMichael Riesch 
330*355a1100SMichael Riesch 	fmt = dw_mipi_csi2rx_find_format(csi2, format->format.code);
331*355a1100SMichael Riesch 	if (!fmt)
332*355a1100SMichael Riesch 		format->format = default_format;
333*355a1100SMichael Riesch 
334*355a1100SMichael Riesch 	*sink = format->format;
335*355a1100SMichael Riesch 
336*355a1100SMichael Riesch 	/* propagate the format to the source pad */
337*355a1100SMichael Riesch 	src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
338*355a1100SMichael Riesch 							   format->stream);
339*355a1100SMichael Riesch 	if (!src)
340*355a1100SMichael Riesch 		return -EINVAL;
341*355a1100SMichael Riesch 
342*355a1100SMichael Riesch 	*src = *sink;
343*355a1100SMichael Riesch 
344*355a1100SMichael Riesch 	return 0;
345*355a1100SMichael Riesch }
346*355a1100SMichael Riesch 
347*355a1100SMichael Riesch static int dw_mipi_csi2rx_set_routing(struct v4l2_subdev *sd,
348*355a1100SMichael Riesch 				      struct v4l2_subdev_state *state,
349*355a1100SMichael Riesch 				      enum v4l2_subdev_format_whence which,
350*355a1100SMichael Riesch 				      struct v4l2_subdev_krouting *routing)
351*355a1100SMichael Riesch {
352*355a1100SMichael Riesch 	int ret;
353*355a1100SMichael Riesch 
354*355a1100SMichael Riesch 	ret = v4l2_subdev_routing_validate(sd, routing,
355*355a1100SMichael Riesch 					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
356*355a1100SMichael Riesch 	if (ret)
357*355a1100SMichael Riesch 		return ret;
358*355a1100SMichael Riesch 
359*355a1100SMichael Riesch 	return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
360*355a1100SMichael Riesch 						&default_format);
361*355a1100SMichael Riesch }
362*355a1100SMichael Riesch 
363*355a1100SMichael Riesch static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd,
364*355a1100SMichael Riesch 					 struct v4l2_subdev_state *state,
365*355a1100SMichael Riesch 					 u32 pad, u64 streams_mask)
366*355a1100SMichael Riesch {
367*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
368*355a1100SMichael Riesch 	struct v4l2_subdev *remote_sd;
369*355a1100SMichael Riesch 	struct media_pad *sink_pad, *remote_pad;
370*355a1100SMichael Riesch 	struct device *dev = csi2->dev;
371*355a1100SMichael Riesch 	u64 mask;
372*355a1100SMichael Riesch 	int ret;
373*355a1100SMichael Riesch 
374*355a1100SMichael Riesch 	sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
375*355a1100SMichael Riesch 	remote_pad = media_pad_remote_pad_first(sink_pad);
376*355a1100SMichael Riesch 	remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
377*355a1100SMichael Riesch 
378*355a1100SMichael Riesch 	mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
379*355a1100SMichael Riesch 					       DW_MIPI_CSI2RX_PAD_SRC,
380*355a1100SMichael Riesch 					       &streams_mask);
381*355a1100SMichael Riesch 
382*355a1100SMichael Riesch 	ret = pm_runtime_resume_and_get(dev);
383*355a1100SMichael Riesch 	if (ret)
384*355a1100SMichael Riesch 		goto err;
385*355a1100SMichael Riesch 
386*355a1100SMichael Riesch 	ret = dw_mipi_csi2rx_start(csi2);
387*355a1100SMichael Riesch 	if (ret) {
388*355a1100SMichael Riesch 		dev_err(dev, "failed to enable CSI hardware\n");
389*355a1100SMichael Riesch 		goto err_pm_runtime_put;
390*355a1100SMichael Riesch 	}
391*355a1100SMichael Riesch 
392*355a1100SMichael Riesch 	ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
393*355a1100SMichael Riesch 	if (ret)
394*355a1100SMichael Riesch 		goto err_csi_stop;
395*355a1100SMichael Riesch 
396*355a1100SMichael Riesch 	return 0;
397*355a1100SMichael Riesch 
398*355a1100SMichael Riesch err_csi_stop:
399*355a1100SMichael Riesch 	dw_mipi_csi2rx_stop(csi2);
400*355a1100SMichael Riesch err_pm_runtime_put:
401*355a1100SMichael Riesch 	pm_runtime_put(dev);
402*355a1100SMichael Riesch err:
403*355a1100SMichael Riesch 	return ret;
404*355a1100SMichael Riesch }
405*355a1100SMichael Riesch 
406*355a1100SMichael Riesch static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
407*355a1100SMichael Riesch 					  struct v4l2_subdev_state *state,
408*355a1100SMichael Riesch 					  u32 pad, u64 streams_mask)
409*355a1100SMichael Riesch {
410*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd);
411*355a1100SMichael Riesch 	struct v4l2_subdev *remote_sd;
412*355a1100SMichael Riesch 	struct media_pad *sink_pad, *remote_pad;
413*355a1100SMichael Riesch 	struct device *dev = csi2->dev;
414*355a1100SMichael Riesch 	u64 mask;
415*355a1100SMichael Riesch 	int ret;
416*355a1100SMichael Riesch 
417*355a1100SMichael Riesch 	sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK];
418*355a1100SMichael Riesch 	remote_pad = media_pad_remote_pad_first(sink_pad);
419*355a1100SMichael Riesch 	remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
420*355a1100SMichael Riesch 
421*355a1100SMichael Riesch 	mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK,
422*355a1100SMichael Riesch 					       DW_MIPI_CSI2RX_PAD_SRC,
423*355a1100SMichael Riesch 					       &streams_mask);
424*355a1100SMichael Riesch 
425*355a1100SMichael Riesch 	ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
426*355a1100SMichael Riesch 
427*355a1100SMichael Riesch 	dw_mipi_csi2rx_stop(csi2);
428*355a1100SMichael Riesch 
429*355a1100SMichael Riesch 	pm_runtime_put(dev);
430*355a1100SMichael Riesch 
431*355a1100SMichael Riesch 	return ret;
432*355a1100SMichael Riesch }
433*355a1100SMichael Riesch 
434*355a1100SMichael Riesch static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = {
435*355a1100SMichael Riesch 	.enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code,
436*355a1100SMichael Riesch 	.get_fmt = v4l2_subdev_get_fmt,
437*355a1100SMichael Riesch 	.set_fmt = dw_mipi_csi2rx_set_fmt,
438*355a1100SMichael Riesch 	.set_routing = dw_mipi_csi2rx_set_routing,
439*355a1100SMichael Riesch 	.enable_streams = dw_mipi_csi2rx_enable_streams,
440*355a1100SMichael Riesch 	.disable_streams = dw_mipi_csi2rx_disable_streams,
441*355a1100SMichael Riesch };
442*355a1100SMichael Riesch 
443*355a1100SMichael Riesch static const struct v4l2_subdev_ops dw_mipi_csi2rx_ops = {
444*355a1100SMichael Riesch 	.pad = &dw_mipi_csi2rx_pad_ops,
445*355a1100SMichael Riesch };
446*355a1100SMichael Riesch 
447*355a1100SMichael Riesch static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd,
448*355a1100SMichael Riesch 				     struct v4l2_subdev_state *state)
449*355a1100SMichael Riesch {
450*355a1100SMichael Riesch 	struct v4l2_subdev_route routes[] = {
451*355a1100SMichael Riesch 		{
452*355a1100SMichael Riesch 			.sink_pad = DW_MIPI_CSI2RX_PAD_SINK,
453*355a1100SMichael Riesch 			.sink_stream = 0,
454*355a1100SMichael Riesch 			.source_pad = DW_MIPI_CSI2RX_PAD_SRC,
455*355a1100SMichael Riesch 			.source_stream = 0,
456*355a1100SMichael Riesch 			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
457*355a1100SMichael Riesch 		},
458*355a1100SMichael Riesch 	};
459*355a1100SMichael Riesch 	struct v4l2_subdev_krouting routing = {
460*355a1100SMichael Riesch 		.len_routes = ARRAY_SIZE(routes),
461*355a1100SMichael Riesch 		.num_routes = ARRAY_SIZE(routes),
462*355a1100SMichael Riesch 		.routes = routes,
463*355a1100SMichael Riesch 	};
464*355a1100SMichael Riesch 
465*355a1100SMichael Riesch 	return v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
466*355a1100SMichael Riesch 						&default_format);
467*355a1100SMichael Riesch }
468*355a1100SMichael Riesch 
469*355a1100SMichael Riesch static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = {
470*355a1100SMichael Riesch 	.init_state = dw_mipi_csi2rx_init_state,
471*355a1100SMichael Riesch };
472*355a1100SMichael Riesch 
473*355a1100SMichael Riesch static int dw_mipi_csi2rx_notifier_bound(struct v4l2_async_notifier *notifier,
474*355a1100SMichael Riesch 					 struct v4l2_subdev *sd,
475*355a1100SMichael Riesch 					 struct v4l2_async_connection *asd)
476*355a1100SMichael Riesch {
477*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 =
478*355a1100SMichael Riesch 		container_of(notifier, struct dw_mipi_csi2rx_device, notifier);
479*355a1100SMichael Riesch 	struct media_pad *sink_pad = &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK];
480*355a1100SMichael Riesch 	int ret;
481*355a1100SMichael Riesch 
482*355a1100SMichael Riesch 	ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
483*355a1100SMichael Riesch 					      MEDIA_LNK_FL_ENABLED);
484*355a1100SMichael Riesch 	if (ret) {
485*355a1100SMichael Riesch 		dev_err(csi2->dev, "failed to link source pad of %s\n",
486*355a1100SMichael Riesch 			sd->name);
487*355a1100SMichael Riesch 		return ret;
488*355a1100SMichael Riesch 	}
489*355a1100SMichael Riesch 
490*355a1100SMichael Riesch 	return 0;
491*355a1100SMichael Riesch }
492*355a1100SMichael Riesch 
493*355a1100SMichael Riesch static const struct v4l2_async_notifier_operations dw_mipi_csi2rx_notifier_ops = {
494*355a1100SMichael Riesch 	.bound = dw_mipi_csi2rx_notifier_bound,
495*355a1100SMichael Riesch };
496*355a1100SMichael Riesch 
497*355a1100SMichael Riesch static int dw_mipi_csi2rx_register_notifier(struct dw_mipi_csi2rx_device *csi2)
498*355a1100SMichael Riesch {
499*355a1100SMichael Riesch 	struct v4l2_async_connection *asd;
500*355a1100SMichael Riesch 	struct v4l2_async_notifier *ntf = &csi2->notifier;
501*355a1100SMichael Riesch 	struct v4l2_fwnode_endpoint vep;
502*355a1100SMichael Riesch 	struct v4l2_subdev *sd = &csi2->sd;
503*355a1100SMichael Riesch 	struct device *dev = csi2->dev;
504*355a1100SMichael Riesch 	int ret;
505*355a1100SMichael Riesch 
506*355a1100SMichael Riesch 	struct fwnode_handle *ep __free(fwnode_handle) =
507*355a1100SMichael Riesch 		fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
508*355a1100SMichael Riesch 	if (!ep)
509*355a1100SMichael Riesch 		return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n");
510*355a1100SMichael Riesch 
511*355a1100SMichael Riesch 	vep.bus_type = V4L2_MBUS_UNKNOWN;
512*355a1100SMichael Riesch 	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
513*355a1100SMichael Riesch 	if (ret)
514*355a1100SMichael Riesch 		return dev_err_probe(dev, ret, "failed to parse endpoint\n");
515*355a1100SMichael Riesch 
516*355a1100SMichael Riesch 	if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
517*355a1100SMichael Riesch 	    vep.bus_type != V4L2_MBUS_CSI2_CPHY)
518*355a1100SMichael Riesch 		return dev_err_probe(dev, -EINVAL,
519*355a1100SMichael Riesch 				     "invalid bus type of endpoint\n");
520*355a1100SMichael Riesch 
521*355a1100SMichael Riesch 	csi2->bus_type = vep.bus_type;
522*355a1100SMichael Riesch 	csi2->lanes_num = vep.bus.mipi_csi2.num_data_lanes;
523*355a1100SMichael Riesch 
524*355a1100SMichael Riesch 	v4l2_async_subdev_nf_init(ntf, sd);
525*355a1100SMichael Riesch 	ntf->ops = &dw_mipi_csi2rx_notifier_ops;
526*355a1100SMichael Riesch 
527*355a1100SMichael Riesch 	asd = v4l2_async_nf_add_fwnode_remote(ntf, ep,
528*355a1100SMichael Riesch 					      struct v4l2_async_connection);
529*355a1100SMichael Riesch 	if (IS_ERR(asd)) {
530*355a1100SMichael Riesch 		ret = PTR_ERR(asd);
531*355a1100SMichael Riesch 		goto err_nf_cleanup;
532*355a1100SMichael Riesch 	}
533*355a1100SMichael Riesch 
534*355a1100SMichael Riesch 	ret = v4l2_async_nf_register(ntf);
535*355a1100SMichael Riesch 	if (ret) {
536*355a1100SMichael Riesch 		ret = dev_err_probe(dev, ret, "failed to register notifier\n");
537*355a1100SMichael Riesch 		goto err_nf_cleanup;
538*355a1100SMichael Riesch 	}
539*355a1100SMichael Riesch 
540*355a1100SMichael Riesch 	return 0;
541*355a1100SMichael Riesch 
542*355a1100SMichael Riesch err_nf_cleanup:
543*355a1100SMichael Riesch 	v4l2_async_nf_cleanup(ntf);
544*355a1100SMichael Riesch 
545*355a1100SMichael Riesch 	return ret;
546*355a1100SMichael Riesch }
547*355a1100SMichael Riesch 
548*355a1100SMichael Riesch static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2)
549*355a1100SMichael Riesch {
550*355a1100SMichael Riesch 	struct media_pad *pads = csi2->pads;
551*355a1100SMichael Riesch 	struct v4l2_subdev *sd = &csi2->sd;
552*355a1100SMichael Riesch 	int ret;
553*355a1100SMichael Riesch 
554*355a1100SMichael Riesch 	ret = dw_mipi_csi2rx_register_notifier(csi2);
555*355a1100SMichael Riesch 	if (ret)
556*355a1100SMichael Riesch 		goto err;
557*355a1100SMichael Riesch 
558*355a1100SMichael Riesch 	v4l2_subdev_init(sd, &dw_mipi_csi2rx_ops);
559*355a1100SMichael Riesch 	sd->dev = csi2->dev;
560*355a1100SMichael Riesch 	sd->entity.ops = &dw_mipi_csi2rx_media_ops;
561*355a1100SMichael Riesch 	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
562*355a1100SMichael Riesch 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
563*355a1100SMichael Riesch 	sd->internal_ops = &dw_mipi_csi2rx_internal_ops;
564*355a1100SMichael Riesch 	snprintf(sd->name, sizeof(sd->name), "dw-mipi-csi2rx %s",
565*355a1100SMichael Riesch 		 dev_name(csi2->dev));
566*355a1100SMichael Riesch 
567*355a1100SMichael Riesch 	pads[DW_MIPI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
568*355a1100SMichael Riesch 					      MEDIA_PAD_FL_MUST_CONNECT;
569*355a1100SMichael Riesch 	pads[DW_MIPI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
570*355a1100SMichael Riesch 	ret = media_entity_pads_init(&sd->entity, DW_MIPI_CSI2RX_PAD_MAX, pads);
571*355a1100SMichael Riesch 	if (ret)
572*355a1100SMichael Riesch 		goto err_notifier_unregister;
573*355a1100SMichael Riesch 
574*355a1100SMichael Riesch 	ret = v4l2_subdev_init_finalize(sd);
575*355a1100SMichael Riesch 	if (ret)
576*355a1100SMichael Riesch 		goto err_entity_cleanup;
577*355a1100SMichael Riesch 
578*355a1100SMichael Riesch 	ret = v4l2_async_register_subdev(sd);
579*355a1100SMichael Riesch 	if (ret) {
580*355a1100SMichael Riesch 		dev_err(sd->dev, "failed to register CSI-2 subdev\n");
581*355a1100SMichael Riesch 		goto err_subdev_cleanup;
582*355a1100SMichael Riesch 	}
583*355a1100SMichael Riesch 
584*355a1100SMichael Riesch 	return 0;
585*355a1100SMichael Riesch 
586*355a1100SMichael Riesch err_subdev_cleanup:
587*355a1100SMichael Riesch 	v4l2_subdev_cleanup(sd);
588*355a1100SMichael Riesch err_entity_cleanup:
589*355a1100SMichael Riesch 	media_entity_cleanup(&sd->entity);
590*355a1100SMichael Riesch err_notifier_unregister:
591*355a1100SMichael Riesch 	v4l2_async_nf_unregister(&csi2->notifier);
592*355a1100SMichael Riesch 	v4l2_async_nf_cleanup(&csi2->notifier);
593*355a1100SMichael Riesch err:
594*355a1100SMichael Riesch 	return ret;
595*355a1100SMichael Riesch }
596*355a1100SMichael Riesch 
597*355a1100SMichael Riesch static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2)
598*355a1100SMichael Riesch {
599*355a1100SMichael Riesch 	struct v4l2_subdev *sd = &csi2->sd;
600*355a1100SMichael Riesch 
601*355a1100SMichael Riesch 	v4l2_async_unregister_subdev(sd);
602*355a1100SMichael Riesch 	v4l2_subdev_cleanup(sd);
603*355a1100SMichael Riesch 	media_entity_cleanup(&sd->entity);
604*355a1100SMichael Riesch 	v4l2_async_nf_unregister(&csi2->notifier);
605*355a1100SMichael Riesch 	v4l2_async_nf_cleanup(&csi2->notifier);
606*355a1100SMichael Riesch }
607*355a1100SMichael Riesch 
608*355a1100SMichael Riesch static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
609*355a1100SMichael Riesch 	{
610*355a1100SMichael Riesch 		.compatible = "rockchip,rk3568-mipi-csi2",
611*355a1100SMichael Riesch 	},
612*355a1100SMichael Riesch 	{}
613*355a1100SMichael Riesch };
614*355a1100SMichael Riesch MODULE_DEVICE_TABLE(of, dw_mipi_csi2rx_of_match);
615*355a1100SMichael Riesch 
616*355a1100SMichael Riesch static int dw_mipi_csi2rx_probe(struct platform_device *pdev)
617*355a1100SMichael Riesch {
618*355a1100SMichael Riesch 	struct device *dev = &pdev->dev;
619*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2;
620*355a1100SMichael Riesch 	int ret;
621*355a1100SMichael Riesch 
622*355a1100SMichael Riesch 	csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL);
623*355a1100SMichael Riesch 	if (!csi2)
624*355a1100SMichael Riesch 		return -ENOMEM;
625*355a1100SMichael Riesch 	csi2->dev = dev;
626*355a1100SMichael Riesch 	dev_set_drvdata(dev, csi2);
627*355a1100SMichael Riesch 
628*355a1100SMichael Riesch 	csi2->base_addr = devm_platform_ioremap_resource(pdev, 0);
629*355a1100SMichael Riesch 	if (IS_ERR(csi2->base_addr))
630*355a1100SMichael Riesch 		return PTR_ERR(csi2->base_addr);
631*355a1100SMichael Riesch 
632*355a1100SMichael Riesch 	ret = devm_clk_bulk_get_all(dev, &csi2->clks);
633*355a1100SMichael Riesch 	if (ret != DW_MIPI_CSI2RX_CLKS_MAX)
634*355a1100SMichael Riesch 		return dev_err_probe(dev, -ENODEV, "failed to get clocks\n");
635*355a1100SMichael Riesch 	csi2->clks_num = ret;
636*355a1100SMichael Riesch 
637*355a1100SMichael Riesch 	csi2->phy = devm_phy_get(dev, NULL);
638*355a1100SMichael Riesch 	if (IS_ERR(csi2->phy))
639*355a1100SMichael Riesch 		return dev_err_probe(dev, PTR_ERR(csi2->phy),
640*355a1100SMichael Riesch 				     "failed to get MIPI CSI-2 PHY\n");
641*355a1100SMichael Riesch 
642*355a1100SMichael Riesch 	csi2->reset = devm_reset_control_get_exclusive(dev, NULL);
643*355a1100SMichael Riesch 	if (IS_ERR(csi2->reset))
644*355a1100SMichael Riesch 		return dev_err_probe(dev, PTR_ERR(csi2->reset),
645*355a1100SMichael Riesch 				     "failed to get reset\n");
646*355a1100SMichael Riesch 
647*355a1100SMichael Riesch 	csi2->formats = formats;
648*355a1100SMichael Riesch 	csi2->formats_num = ARRAY_SIZE(formats);
649*355a1100SMichael Riesch 
650*355a1100SMichael Riesch 	ret = devm_pm_runtime_enable(dev);
651*355a1100SMichael Riesch 	if (ret)
652*355a1100SMichael Riesch 		return dev_err_probe(dev, ret, "failed to enable pm runtime\n");
653*355a1100SMichael Riesch 
654*355a1100SMichael Riesch 	ret = phy_init(csi2->phy);
655*355a1100SMichael Riesch 	if (ret)
656*355a1100SMichael Riesch 		return dev_err_probe(dev, ret,
657*355a1100SMichael Riesch 				     "failed to initialize MIPI CSI-2 PHY\n");
658*355a1100SMichael Riesch 
659*355a1100SMichael Riesch 	ret = dw_mipi_csi2rx_register(csi2);
660*355a1100SMichael Riesch 	if (ret)
661*355a1100SMichael Riesch 		goto err_phy_exit;
662*355a1100SMichael Riesch 
663*355a1100SMichael Riesch 	return 0;
664*355a1100SMichael Riesch 
665*355a1100SMichael Riesch err_phy_exit:
666*355a1100SMichael Riesch 	phy_exit(csi2->phy);
667*355a1100SMichael Riesch 
668*355a1100SMichael Riesch 	return ret;
669*355a1100SMichael Riesch }
670*355a1100SMichael Riesch 
671*355a1100SMichael Riesch static void dw_mipi_csi2rx_remove(struct platform_device *pdev)
672*355a1100SMichael Riesch {
673*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = platform_get_drvdata(pdev);
674*355a1100SMichael Riesch 
675*355a1100SMichael Riesch 	dw_mipi_csi2rx_unregister(csi2);
676*355a1100SMichael Riesch 	phy_exit(csi2->phy);
677*355a1100SMichael Riesch }
678*355a1100SMichael Riesch 
679*355a1100SMichael Riesch static int dw_mipi_csi2rx_runtime_suspend(struct device *dev)
680*355a1100SMichael Riesch {
681*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev);
682*355a1100SMichael Riesch 
683*355a1100SMichael Riesch 	clk_bulk_disable_unprepare(csi2->clks_num, csi2->clks);
684*355a1100SMichael Riesch 
685*355a1100SMichael Riesch 	return 0;
686*355a1100SMichael Riesch }
687*355a1100SMichael Riesch 
688*355a1100SMichael Riesch static int dw_mipi_csi2rx_runtime_resume(struct device *dev)
689*355a1100SMichael Riesch {
690*355a1100SMichael Riesch 	struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev);
691*355a1100SMichael Riesch 	int ret;
692*355a1100SMichael Riesch 
693*355a1100SMichael Riesch 	reset_control_assert(csi2->reset);
694*355a1100SMichael Riesch 	udelay(5);
695*355a1100SMichael Riesch 	reset_control_deassert(csi2->reset);
696*355a1100SMichael Riesch 
697*355a1100SMichael Riesch 	ret = clk_bulk_prepare_enable(csi2->clks_num, csi2->clks);
698*355a1100SMichael Riesch 	if (ret) {
699*355a1100SMichael Riesch 		dev_err(dev, "failed to enable clocks\n");
700*355a1100SMichael Riesch 		return ret;
701*355a1100SMichael Riesch 	}
702*355a1100SMichael Riesch 
703*355a1100SMichael Riesch 	return 0;
704*355a1100SMichael Riesch }
705*355a1100SMichael Riesch 
706*355a1100SMichael Riesch static DEFINE_RUNTIME_DEV_PM_OPS(dw_mipi_csi2rx_pm_ops,
707*355a1100SMichael Riesch 				 dw_mipi_csi2rx_runtime_suspend,
708*355a1100SMichael Riesch 				 dw_mipi_csi2rx_runtime_resume, NULL);
709*355a1100SMichael Riesch 
710*355a1100SMichael Riesch static struct platform_driver dw_mipi_csi2rx_drv = {
711*355a1100SMichael Riesch 	.driver = {
712*355a1100SMichael Riesch 		.name = "dw-mipi-csi2rx",
713*355a1100SMichael Riesch 		.of_match_table = dw_mipi_csi2rx_of_match,
714*355a1100SMichael Riesch 		.pm = pm_ptr(&dw_mipi_csi2rx_pm_ops),
715*355a1100SMichael Riesch 	},
716*355a1100SMichael Riesch 	.probe = dw_mipi_csi2rx_probe,
717*355a1100SMichael Riesch 	.remove = dw_mipi_csi2rx_remove,
718*355a1100SMichael Riesch };
719*355a1100SMichael Riesch module_platform_driver(dw_mipi_csi2rx_drv);
720*355a1100SMichael Riesch 
721*355a1100SMichael Riesch MODULE_DESCRIPTION("Synopsys DesignWare MIPI CSI-2 Receiver platform driver");
722*355a1100SMichael Riesch MODULE_LICENSE("GPL");
723