1
2
3
4
5
6
7
8#include <linux/clk.h>
9#include <linux/clk-provider.h>
10#include <linux/delay.h>
11#include <linux/errno.h>
12#include <linux/interconnect.h>
13#include <linux/interrupt.h>
14#include <linux/io.h>
15#include <linux/kernel.h>
16#include <linux/mfd/syscon.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/of.h>
20#include <linux/of_device.h>
21#include <linux/platform_device.h>
22#include <linux/pm_runtime.h>
23#include <linux/regmap.h>
24#include <linux/regulator/consumer.h>
25#include <linux/reset.h>
26#include <linux/spinlock.h>
27
28#include <media/v4l2-common.h>
29#include <media/v4l2-device.h>
30#include <media/v4l2-fwnode.h>
31#include <media/v4l2-mc.h>
32#include <media/v4l2-subdev.h>
33
34#define MIPI_CSI2_DRIVER_NAME "imx8mq-mipi-csi2"
35#define MIPI_CSI2_SUBDEV_NAME MIPI_CSI2_DRIVER_NAME
36
37#define MIPI_CSI2_PAD_SINK 0
38#define MIPI_CSI2_PAD_SOURCE 1
39#define MIPI_CSI2_PADS_NUM 2
40
41#define MIPI_CSI2_DEF_PIX_WIDTH 640
42#define MIPI_CSI2_DEF_PIX_HEIGHT 480
43
44
45
46
47#define CSI2RX_CFG_NUM_LANES 0x100
48#define CSI2RX_CFG_DISABLE_DATA_LANES 0x104
49#define CSI2RX_BIT_ERR 0x108
50#define CSI2RX_IRQ_STATUS 0x10c
51#define CSI2RX_IRQ_MASK 0x110
52#define CSI2RX_IRQ_MASK_ALL 0x1ff
53#define CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE 0x8
54#define CSI2RX_ULPS_STATUS 0x114
55#define CSI2RX_PPI_ERRSOT_HS 0x118
56#define CSI2RX_PPI_ERRSOTSYNC_HS 0x11c
57#define CSI2RX_PPI_ERRESC 0x120
58#define CSI2RX_PPI_ERRSYNCESC 0x124
59#define CSI2RX_PPI_ERRCONTROL 0x128
60#define CSI2RX_CFG_DISABLE_PAYLOAD_0 0x12c
61#define CSI2RX_CFG_VID_VC_IGNORE 0x180
62#define CSI2RX_CFG_VID_VC 0x184
63#define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL 0x188
64#define CSI2RX_CFG_DISABLE_PAYLOAD_1 0x130
65
66enum {
67 ST_POWERED = 1,
68 ST_STREAMING = 2,
69 ST_SUSPENDED = 4,
70};
71
72enum imx8mq_mipi_csi_clk {
73 CSI2_CLK_CORE,
74 CSI2_CLK_ESC,
75 CSI2_CLK_UI,
76 CSI2_NUM_CLKS,
77};
78
79static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
80 [CSI2_CLK_CORE] = "core",
81 [CSI2_CLK_ESC] = "esc",
82 [CSI2_CLK_UI] = "ui",
83};
84
85#define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
86
87#define GPR_CSI2_1_RX_ENABLE BIT(13)
88#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
89#define GPR_CSI2_1_HSEL BIT(10)
90#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
91#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2)
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106#define CSI2RX_SEND_LEVEL 64
107
108struct csi_state {
109 struct device *dev;
110 void __iomem *regs;
111 struct clk_bulk_data clks[CSI2_NUM_CLKS];
112 struct reset_control *rst;
113 struct regulator *mipi_phy_regulator;
114
115 struct v4l2_subdev sd;
116 struct media_pad pads[MIPI_CSI2_PADS_NUM];
117 struct v4l2_async_notifier notifier;
118 struct v4l2_subdev *src_sd;
119
120 struct v4l2_fwnode_bus_mipi_csi2 bus;
121
122 struct mutex lock;
123 const struct csi2_pix_format *csi2_fmt;
124 struct v4l2_mbus_framefmt format_mbus[MIPI_CSI2_PADS_NUM];
125 u32 state;
126 u32 hs_settle;
127
128 struct regmap *phy_gpr;
129 u8 phy_gpr_reg;
130
131 struct icc_path *icc_path;
132 s32 icc_path_bw;
133};
134
135
136
137
138
139struct csi2_pix_format {
140 u32 code;
141 u8 width;
142};
143
144static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
145
146 {
147 .code = MEDIA_BUS_FMT_SBGGR8_1X8,
148 .width = 8,
149 }, {
150 .code = MEDIA_BUS_FMT_SGBRG8_1X8,
151 .width = 8,
152 }, {
153 .code = MEDIA_BUS_FMT_SGRBG8_1X8,
154 .width = 8,
155 }, {
156 .code = MEDIA_BUS_FMT_SRGGB8_1X8,
157 .width = 8,
158 }, {
159 .code = MEDIA_BUS_FMT_Y8_1X8,
160 .width = 8,
161 }, {
162 .code = MEDIA_BUS_FMT_SBGGR10_1X10,
163 .width = 10,
164 }, {
165 .code = MEDIA_BUS_FMT_SGBRG10_1X10,
166 .width = 10,
167 }, {
168 .code = MEDIA_BUS_FMT_SGRBG10_1X10,
169 .width = 10,
170 }, {
171 .code = MEDIA_BUS_FMT_SRGGB10_1X10,
172 .width = 10,
173 }, {
174 .code = MEDIA_BUS_FMT_Y10_1X10,
175 .width = 10,
176 }, {
177 .code = MEDIA_BUS_FMT_SBGGR12_1X12,
178 .width = 12,
179 }, {
180 .code = MEDIA_BUS_FMT_SGBRG12_1X12,
181 .width = 12,
182 }, {
183 .code = MEDIA_BUS_FMT_SGRBG12_1X12,
184 .width = 12,
185 }, {
186 .code = MEDIA_BUS_FMT_SRGGB12_1X12,
187 .width = 12,
188 }, {
189 .code = MEDIA_BUS_FMT_Y12_1X12,
190 .width = 12,
191 }, {
192 .code = MEDIA_BUS_FMT_SBGGR14_1X14,
193 .width = 14,
194 }, {
195 .code = MEDIA_BUS_FMT_SGBRG14_1X14,
196 .width = 14,
197 }, {
198 .code = MEDIA_BUS_FMT_SGRBG14_1X14,
199 .width = 14,
200 }, {
201 .code = MEDIA_BUS_FMT_SRGGB14_1X14,
202 .width = 14,
203 }, {
204
205 .code = MEDIA_BUS_FMT_YUYV8_2X8,
206 .width = 16,
207 }, {
208 .code = MEDIA_BUS_FMT_YUYV8_1X16,
209 .width = 16,
210 }
211};
212
213static const struct csi2_pix_format *find_csi2_format(u32 code)
214{
215 unsigned int i;
216
217 for (i = 0; i < ARRAY_SIZE(imx8mq_mipi_csi_formats); i++)
218 if (code == imx8mq_mipi_csi_formats[i].code)
219 return &imx8mq_mipi_csi_formats[i];
220 return NULL;
221}
222
223
224
225
226
227static inline void imx8mq_mipi_csi_write(struct csi_state *state, u32 reg, u32 val)
228{
229 writel(val, state->regs + reg);
230}
231
232static int imx8mq_mipi_csi_sw_reset(struct csi_state *state)
233{
234 int ret;
235
236
237
238
239
240
241 ret = reset_control_assert(state->rst);
242 if (ret < 0) {
243 dev_err(state->dev, "Failed to assert resets: %d\n", ret);
244 return ret;
245 }
246
247 return 0;
248}
249
250static void imx8mq_mipi_csi_system_enable(struct csi_state *state, int on)
251{
252 if (!on) {
253 imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
254 return;
255 }
256
257 regmap_update_bits(state->phy_gpr,
258 state->phy_gpr_reg,
259 0x3fff,
260 GPR_CSI2_1_RX_ENABLE |
261 GPR_CSI2_1_VID_INTFC_ENB |
262 GPR_CSI2_1_HSEL |
263 GPR_CSI2_1_CONT_CLK_MODE |
264 GPR_CSI2_1_S_PRG_RXHS_SETTLE(state->hs_settle));
265}
266
267static void imx8mq_mipi_csi_set_params(struct csi_state *state)
268{
269 int lanes = state->bus.num_data_lanes;
270
271 imx8mq_mipi_csi_write(state, CSI2RX_CFG_NUM_LANES, lanes - 1);
272 imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES,
273 (0xf << lanes) & 0xf);
274 imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK, CSI2RX_IRQ_MASK_ALL);
275
276
277
278
279
280
281 imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_VC_IGNORE, 1);
282 imx8mq_mipi_csi_write(state, CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL,
283 CSI2RX_SEND_LEVEL);
284}
285
286static int imx8mq_mipi_csi_clk_enable(struct csi_state *state)
287{
288 return clk_bulk_prepare_enable(CSI2_NUM_CLKS, state->clks);
289}
290
291static void imx8mq_mipi_csi_clk_disable(struct csi_state *state)
292{
293 clk_bulk_disable_unprepare(CSI2_NUM_CLKS, state->clks);
294}
295
296static int imx8mq_mipi_csi_clk_get(struct csi_state *state)
297{
298 unsigned int i;
299
300 for (i = 0; i < CSI2_NUM_CLKS; i++)
301 state->clks[i].id = imx8mq_mipi_csi_clk_id[i];
302
303 return devm_clk_bulk_get(state->dev, CSI2_NUM_CLKS, state->clks);
304}
305
306static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state)
307{
308 s64 link_freq;
309 u32 lane_rate;
310 unsigned long esc_clk_rate;
311 u32 min_ths_settle, max_ths_settle, ths_settle_ns, esc_clk_period_ns;
312
313
314 link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
315 state->csi2_fmt->width,
316 state->bus.num_data_lanes * 2);
317 if (link_freq < 0) {
318 dev_err(state->dev, "Unable to obtain link frequency: %d\n",
319 (int)link_freq);
320 return link_freq;
321 }
322
323 lane_rate = link_freq * 2;
324 if (lane_rate < 80000000 || lane_rate > 1500000000) {
325 dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate);
326 return -EINVAL;
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343 esc_clk_rate = clk_get_rate(state->clks[CSI2_CLK_ESC].clk);
344 if (!esc_clk_rate) {
345 dev_err(state->dev, "Could not get esc clock rate.\n");
346 return -EINVAL;
347 }
348
349 dev_dbg(state->dev, "esc clk rate: %lu\n", esc_clk_rate);
350 esc_clk_period_ns = 1000000000 / esc_clk_rate;
351
352 min_ths_settle = 85 + 6 * 1000000 / (lane_rate / 1000);
353 max_ths_settle = 140 + 10 * 1000000 / (lane_rate / 1000);
354 ths_settle_ns = (min_ths_settle + max_ths_settle) / 2;
355
356 state->hs_settle = ths_settle_ns / esc_clk_period_ns - 1;
357
358 dev_dbg(state->dev, "lane rate %u Ths_settle %u hs_settle %u\n",
359 lane_rate, ths_settle_ns, state->hs_settle);
360
361 return 0;
362}
363
364static int imx8mq_mipi_csi_start_stream(struct csi_state *state)
365{
366 int ret;
367
368 ret = imx8mq_mipi_csi_sw_reset(state);
369 if (ret)
370 return ret;
371
372 imx8mq_mipi_csi_set_params(state);
373 ret = imx8mq_mipi_csi_calc_hs_settle(state);
374 if (ret)
375 return ret;
376
377 imx8mq_mipi_csi_system_enable(state, true);
378
379 return 0;
380}
381
382static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
383{
384 imx8mq_mipi_csi_system_enable(state, false);
385}
386
387
388
389
390
391static struct csi_state *mipi_sd_to_csi2_state(struct v4l2_subdev *sdev)
392{
393 return container_of(sdev, struct csi_state, sd);
394}
395
396static int imx8mq_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable)
397{
398 struct csi_state *state = mipi_sd_to_csi2_state(sd);
399 int ret = 0;
400
401 imx8mq_mipi_csi_write(state, CSI2RX_IRQ_MASK,
402 CSI2RX_IRQ_MASK_ULPS_STATUS_CHANGE);
403
404 if (enable) {
405 ret = pm_runtime_resume_and_get(state->dev);
406 if (ret < 0)
407 return ret;
408 }
409
410 mutex_lock(&state->lock);
411
412 if (enable) {
413 if (state->state & ST_SUSPENDED) {
414 ret = -EBUSY;
415 goto unlock;
416 }
417
418 ret = imx8mq_mipi_csi_start_stream(state);
419 if (ret < 0)
420 goto unlock;
421
422 ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
423 if (ret < 0)
424 goto unlock;
425
426 state->state |= ST_STREAMING;
427 } else {
428 v4l2_subdev_call(state->src_sd, video, s_stream, 0);
429 imx8mq_mipi_csi_stop_stream(state);
430 state->state &= ~ST_STREAMING;
431 }
432
433unlock:
434 mutex_unlock(&state->lock);
435
436 if (!enable || ret < 0)
437 pm_runtime_put(state->dev);
438
439 return ret;
440}
441
442static struct v4l2_mbus_framefmt *
443imx8mq_mipi_csi_get_format(struct csi_state *state,
444 struct v4l2_subdev_state *sd_state,
445 enum v4l2_subdev_format_whence which,
446 unsigned int pad)
447{
448 if (which == V4L2_SUBDEV_FORMAT_TRY)
449 return v4l2_subdev_get_try_format(&state->sd, sd_state, pad);
450
451 return &state->format_mbus[pad];
452}
453
454static int imx8mq_mipi_csi_init_cfg(struct v4l2_subdev *sd,
455 struct v4l2_subdev_state *sd_state)
456{
457 struct csi_state *state = mipi_sd_to_csi2_state(sd);
458 struct v4l2_mbus_framefmt *fmt_sink;
459 struct v4l2_mbus_framefmt *fmt_source;
460 enum v4l2_subdev_format_whence which;
461
462 which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
463 fmt_sink = imx8mq_mipi_csi_get_format(state, sd_state, which,
464 MIPI_CSI2_PAD_SINK);
465
466 fmt_sink->code = MEDIA_BUS_FMT_SGBRG10_1X10;
467 fmt_sink->width = MIPI_CSI2_DEF_PIX_WIDTH;
468 fmt_sink->height = MIPI_CSI2_DEF_PIX_HEIGHT;
469 fmt_sink->field = V4L2_FIELD_NONE;
470
471 fmt_sink->colorspace = V4L2_COLORSPACE_RAW;
472 fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
473 fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
474 fmt_sink->quantization =
475 V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
476 fmt_sink->ycbcr_enc);
477
478 fmt_source = imx8mq_mipi_csi_get_format(state, sd_state, which,
479 MIPI_CSI2_PAD_SOURCE);
480 *fmt_source = *fmt_sink;
481
482 return 0;
483}
484
485static int imx8mq_mipi_csi_get_fmt(struct v4l2_subdev *sd,
486 struct v4l2_subdev_state *sd_state,
487 struct v4l2_subdev_format *sdformat)
488{
489 struct csi_state *state = mipi_sd_to_csi2_state(sd);
490 struct v4l2_mbus_framefmt *fmt;
491
492 fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
493 sdformat->pad);
494
495 mutex_lock(&state->lock);
496
497 sdformat->format = *fmt;
498
499 mutex_unlock(&state->lock);
500
501 return 0;
502}
503
504static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
505 struct v4l2_subdev_state *sd_state,
506 struct v4l2_subdev_mbus_code_enum *code)
507{
508 struct csi_state *state = mipi_sd_to_csi2_state(sd);
509
510
511
512
513
514 if (code->pad == MIPI_CSI2_PAD_SOURCE) {
515 struct v4l2_mbus_framefmt *fmt;
516
517 if (code->index > 0)
518 return -EINVAL;
519
520 fmt = imx8mq_mipi_csi_get_format(state, sd_state, code->which,
521 code->pad);
522 code->code = fmt->code;
523 return 0;
524 }
525
526 if (code->pad != MIPI_CSI2_PAD_SINK)
527 return -EINVAL;
528
529 if (code->index >= ARRAY_SIZE(imx8mq_mipi_csi_formats))
530 return -EINVAL;
531
532 code->code = imx8mq_mipi_csi_formats[code->index].code;
533
534 return 0;
535}
536
537static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd,
538 struct v4l2_subdev_state *sd_state,
539 struct v4l2_subdev_format *sdformat)
540{
541 struct csi_state *state = mipi_sd_to_csi2_state(sd);
542 struct csi2_pix_format const *csi2_fmt;
543 struct v4l2_mbus_framefmt *fmt;
544
545
546
547
548
549 if (sdformat->pad == MIPI_CSI2_PAD_SOURCE)
550 return imx8mq_mipi_csi_get_fmt(sd, sd_state, sdformat);
551
552 if (sdformat->pad != MIPI_CSI2_PAD_SINK)
553 return -EINVAL;
554
555 csi2_fmt = find_csi2_format(sdformat->format.code);
556 if (!csi2_fmt)
557 csi2_fmt = &imx8mq_mipi_csi_formats[0];
558
559 fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
560 sdformat->pad);
561
562 mutex_lock(&state->lock);
563
564 fmt->code = csi2_fmt->code;
565 fmt->width = sdformat->format.width;
566 fmt->height = sdformat->format.height;
567
568 sdformat->format = *fmt;
569
570
571 fmt = imx8mq_mipi_csi_get_format(state, sd_state, sdformat->which,
572 MIPI_CSI2_PAD_SOURCE);
573 *fmt = sdformat->format;
574
575
576 if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
577 state->csi2_fmt = csi2_fmt;
578
579 mutex_unlock(&state->lock);
580
581 return 0;
582}
583
584static const struct v4l2_subdev_video_ops imx8mq_mipi_csi_video_ops = {
585 .s_stream = imx8mq_mipi_csi_s_stream,
586};
587
588static const struct v4l2_subdev_pad_ops imx8mq_mipi_csi_pad_ops = {
589 .init_cfg = imx8mq_mipi_csi_init_cfg,
590 .enum_mbus_code = imx8mq_mipi_csi_enum_mbus_code,
591 .get_fmt = imx8mq_mipi_csi_get_fmt,
592 .set_fmt = imx8mq_mipi_csi_set_fmt,
593};
594
595static const struct v4l2_subdev_ops imx8mq_mipi_csi_subdev_ops = {
596 .video = &imx8mq_mipi_csi_video_ops,
597 .pad = &imx8mq_mipi_csi_pad_ops,
598};
599
600
601
602
603
604static const struct media_entity_operations imx8mq_mipi_csi_entity_ops = {
605 .link_validate = v4l2_subdev_link_validate,
606 .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
607};
608
609
610
611
612
613static struct csi_state *
614mipi_notifier_to_csi2_state(struct v4l2_async_notifier *n)
615{
616 return container_of(n, struct csi_state, notifier);
617}
618
619static int imx8mq_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
620 struct v4l2_subdev *sd,
621 struct v4l2_async_subdev *asd)
622{
623 struct csi_state *state = mipi_notifier_to_csi2_state(notifier);
624 struct media_pad *sink = &state->sd.entity.pads[MIPI_CSI2_PAD_SINK];
625
626 state->src_sd = sd;
627
628 return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
629 MEDIA_LNK_FL_IMMUTABLE);
630}
631
632static const struct v4l2_async_notifier_operations imx8mq_mipi_csi_notify_ops = {
633 .bound = imx8mq_mipi_csi_notify_bound,
634};
635
636static int imx8mq_mipi_csi_async_register(struct csi_state *state)
637{
638 struct v4l2_fwnode_endpoint vep = {
639 .bus_type = V4L2_MBUS_CSI2_DPHY,
640 };
641 struct v4l2_async_subdev *asd;
642 struct fwnode_handle *ep;
643 unsigned int i;
644 int ret;
645
646 v4l2_async_notifier_init(&state->notifier);
647
648 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
649 FWNODE_GRAPH_ENDPOINT_NEXT);
650 if (!ep)
651 return -ENOTCONN;
652
653 ret = v4l2_fwnode_endpoint_parse(ep, &vep);
654 if (ret)
655 goto err_parse;
656
657 for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
658 if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
659 dev_err(state->dev,
660 "data lanes reordering is not supported");
661 ret = -EINVAL;
662 goto err_parse;
663 }
664 }
665
666 state->bus = vep.bus.mipi_csi2;
667
668 dev_dbg(state->dev, "data lanes: %d flags: 0x%08x\n",
669 state->bus.num_data_lanes,
670 state->bus.flags);
671
672 asd = v4l2_async_notifier_add_fwnode_remote_subdev(&state->notifier,
673 ep, struct v4l2_async_subdev);
674 if (IS_ERR(asd)) {
675 ret = PTR_ERR(asd);
676 goto err_parse;
677 }
678
679 fwnode_handle_put(ep);
680
681 state->notifier.ops = &imx8mq_mipi_csi_notify_ops;
682
683 ret = v4l2_async_subdev_notifier_register(&state->sd, &state->notifier);
684 if (ret)
685 return ret;
686
687 return v4l2_async_register_subdev(&state->sd);
688
689err_parse:
690 fwnode_handle_put(ep);
691
692 return ret;
693}
694
695
696
697
698
699static int imx8mq_mipi_csi_pm_suspend(struct device *dev, bool runtime)
700{
701 struct v4l2_subdev *sd = dev_get_drvdata(dev);
702 struct csi_state *state = mipi_sd_to_csi2_state(sd);
703 int ret = 0;
704
705 mutex_lock(&state->lock);
706
707 if (state->state & ST_POWERED) {
708 imx8mq_mipi_csi_stop_stream(state);
709 imx8mq_mipi_csi_clk_disable(state);
710 state->state &= ~ST_POWERED;
711 if (!runtime)
712 state->state |= ST_SUSPENDED;
713 }
714
715 mutex_unlock(&state->lock);
716
717 ret = icc_set_bw(state->icc_path, 0, 0);
718 if (ret)
719 dev_err(dev, "icc_set_bw failed with %d\n", ret);
720
721 return ret ? -EAGAIN : 0;
722}
723
724static int imx8mq_mipi_csi_pm_resume(struct device *dev, bool runtime)
725{
726 struct v4l2_subdev *sd = dev_get_drvdata(dev);
727 struct csi_state *state = mipi_sd_to_csi2_state(sd);
728 int ret = 0;
729
730 ret = icc_set_bw(state->icc_path, 0, state->icc_path_bw);
731 if (ret) {
732 dev_err(dev, "icc_set_bw failed with %d\n", ret);
733 return ret;
734 }
735
736 mutex_lock(&state->lock);
737
738 if (!runtime && !(state->state & ST_SUSPENDED))
739 goto unlock;
740
741 if (!(state->state & ST_POWERED)) {
742 state->state |= ST_POWERED;
743 ret = imx8mq_mipi_csi_clk_enable(state);
744 }
745 if (state->state & ST_STREAMING) {
746 ret = imx8mq_mipi_csi_start_stream(state);
747 if (ret)
748 goto unlock;
749 }
750
751 state->state &= ~ST_SUSPENDED;
752
753unlock:
754 mutex_unlock(&state->lock);
755
756 return ret ? -EAGAIN : 0;
757}
758
759static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev)
760{
761 return imx8mq_mipi_csi_pm_suspend(dev, false);
762}
763
764static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev)
765{
766 return imx8mq_mipi_csi_pm_resume(dev, false);
767}
768
769static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev)
770{
771 return imx8mq_mipi_csi_pm_suspend(dev, true);
772}
773
774static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev)
775{
776 return imx8mq_mipi_csi_pm_resume(dev, true);
777}
778
779static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = {
780 SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
781 imx8mq_mipi_csi_runtime_resume,
782 NULL)
783 SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
784};
785
786
787
788
789
790static int imx8mq_mipi_csi_subdev_init(struct csi_state *state)
791{
792 struct v4l2_subdev *sd = &state->sd;
793
794 v4l2_subdev_init(sd, &imx8mq_mipi_csi_subdev_ops);
795 sd->owner = THIS_MODULE;
796 snprintf(sd->name, sizeof(sd->name), "%s %s",
797 MIPI_CSI2_SUBDEV_NAME, dev_name(state->dev));
798
799 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
800
801 sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
802 sd->entity.ops = &imx8mq_mipi_csi_entity_ops;
803
804 sd->dev = state->dev;
805
806 state->csi2_fmt = &imx8mq_mipi_csi_formats[0];
807 imx8mq_mipi_csi_init_cfg(sd, NULL);
808
809 state->pads[MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
810 | MEDIA_PAD_FL_MUST_CONNECT;
811 state->pads[MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
812 | MEDIA_PAD_FL_MUST_CONNECT;
813 return media_entity_pads_init(&sd->entity, MIPI_CSI2_PADS_NUM,
814 state->pads);
815}
816
817static void imx8mq_mipi_csi_release_icc(struct platform_device *pdev)
818{
819 struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
820 struct csi_state *state = mipi_sd_to_csi2_state(sd);
821
822 icc_put(state->icc_path);
823}
824
825static int imx8mq_mipi_csi_init_icc(struct platform_device *pdev)
826{
827 struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
828 struct csi_state *state = mipi_sd_to_csi2_state(sd);
829
830
831 state->icc_path = of_icc_get(&pdev->dev, "dram");
832 if (IS_ERR_OR_NULL(state->icc_path))
833 return PTR_ERR_OR_ZERO(state->icc_path);
834
835 state->icc_path_bw = MBps_to_icc(700);
836
837 return 0;
838}
839
840static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
841{
842 struct device *dev = state->dev;
843 struct device_node *np = state->dev->of_node;
844 struct device_node *node;
845 phandle ph;
846 u32 out_val[2];
847 int ret = 0;
848
849 state->rst = devm_reset_control_array_get_exclusive(dev);
850 if (IS_ERR(state->rst)) {
851 dev_err(dev, "Failed to get reset: %pe\n", state->rst);
852 return PTR_ERR(state->rst);
853 }
854
855 ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
856 ARRAY_SIZE(out_val));
857 if (ret) {
858 dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret);
859 return ret;
860 }
861
862 ph = *out_val;
863
864 node = of_find_node_by_phandle(ph);
865 if (!node) {
866 dev_err(dev, "Error finding node by phandle\n");
867 return -ENODEV;
868 }
869 state->phy_gpr = syscon_node_to_regmap(node);
870 of_node_put(node);
871 if (IS_ERR(state->phy_gpr)) {
872 dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr);
873 return PTR_ERR(state->phy_gpr);
874 }
875
876 state->phy_gpr_reg = out_val[1];
877 dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg);
878
879 return ret;
880}
881
882static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
883{
884 struct device *dev = &pdev->dev;
885 struct csi_state *state;
886 int ret;
887
888 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
889 if (!state)
890 return -ENOMEM;
891
892 state->dev = dev;
893
894 ret = imx8mq_mipi_csi_parse_dt(state);
895 if (ret < 0) {
896 dev_err(dev, "Failed to parse device tree: %d\n", ret);
897 return ret;
898 }
899
900
901 state->regs = devm_platform_ioremap_resource(pdev, 0);
902 if (IS_ERR(state->regs))
903 return PTR_ERR(state->regs);
904
905 ret = imx8mq_mipi_csi_clk_get(state);
906 if (ret < 0)
907 return ret;
908
909 platform_set_drvdata(pdev, &state->sd);
910
911 mutex_init(&state->lock);
912
913 ret = imx8mq_mipi_csi_subdev_init(state);
914 if (ret < 0)
915 goto mutex;
916
917 ret = imx8mq_mipi_csi_init_icc(pdev);
918 if (ret)
919 goto mutex;
920
921
922 pm_runtime_enable(dev);
923 if (!pm_runtime_enabled(dev)) {
924 ret = imx8mq_mipi_csi_pm_resume(dev, true);
925 if (ret < 0)
926 goto icc;
927 }
928
929 ret = imx8mq_mipi_csi_async_register(state);
930 if (ret < 0)
931 goto cleanup;
932
933 return 0;
934
935cleanup:
936 pm_runtime_disable(&pdev->dev);
937 imx8mq_mipi_csi_pm_suspend(&pdev->dev, true);
938
939 media_entity_cleanup(&state->sd.entity);
940 v4l2_async_notifier_unregister(&state->notifier);
941 v4l2_async_notifier_cleanup(&state->notifier);
942 v4l2_async_unregister_subdev(&state->sd);
943icc:
944 imx8mq_mipi_csi_release_icc(pdev);
945mutex:
946 mutex_destroy(&state->lock);
947
948 return ret;
949}
950
951static int imx8mq_mipi_csi_remove(struct platform_device *pdev)
952{
953 struct v4l2_subdev *sd = platform_get_drvdata(pdev);
954 struct csi_state *state = mipi_sd_to_csi2_state(sd);
955
956 v4l2_async_notifier_unregister(&state->notifier);
957 v4l2_async_notifier_cleanup(&state->notifier);
958 v4l2_async_unregister_subdev(&state->sd);
959
960 pm_runtime_disable(&pdev->dev);
961 imx8mq_mipi_csi_pm_suspend(&pdev->dev, true);
962 media_entity_cleanup(&state->sd.entity);
963 mutex_destroy(&state->lock);
964 pm_runtime_set_suspended(&pdev->dev);
965 imx8mq_mipi_csi_release_icc(pdev);
966
967 return 0;
968}
969
970static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
971 { .compatible = "fsl,imx8mq-mipi-csi2", },
972 { },
973};
974MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
975
976static struct platform_driver imx8mq_mipi_csi_driver = {
977 .probe = imx8mq_mipi_csi_probe,
978 .remove = imx8mq_mipi_csi_remove,
979 .driver = {
980 .of_match_table = imx8mq_mipi_csi_of_match,
981 .name = MIPI_CSI2_DRIVER_NAME,
982 .pm = &imx8mq_mipi_csi_pm_ops,
983 },
984};
985
986module_platform_driver(imx8mq_mipi_csi_driver);
987
988MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
989MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
990MODULE_LICENSE("GPL v2");
991MODULE_ALIAS("platform:imx8mq-mipi-csi2");
992