1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright 2019-2020 NXP
7 #include <linux/device.h>
8 #include <linux/errno.h>
9 #include <linux/kernel.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/property.h>
17 #include <linux/slab.h>
18 #include <linux/string.h>
19 #include <linux/types.h>
21 #include <media/media-device.h>
22 #include <media/v4l2-async.h>
23 #include <media/v4l2-device.h>
24 #include <media/v4l2-mc.h>
26 #include "imx8-isi-core.h"
28 /* -----------------------------------------------------------------------------
32 struct mxc_isi_async_subdev
{
33 struct v4l2_async_connection asd
;
37 static inline struct mxc_isi_async_subdev
*
38 asd_to_mxc_isi_async_subdev(struct v4l2_async_connection
*asd
)
40 return container_of(asd
, struct mxc_isi_async_subdev
, asd
);
43 static inline struct mxc_isi_dev
*
44 notifier_to_mxc_isi_dev(struct v4l2_async_notifier
*n
)
46 return container_of(n
, struct mxc_isi_dev
, notifier
);
49 static int mxc_isi_async_notifier_bound(struct v4l2_async_notifier
*notifier
,
50 struct v4l2_subdev
*sd
,
51 struct v4l2_async_connection
*asc
)
53 const unsigned int link_flags
= MEDIA_LNK_FL_IMMUTABLE
54 | MEDIA_LNK_FL_ENABLED
;
55 struct mxc_isi_dev
*isi
= notifier_to_mxc_isi_dev(notifier
);
56 struct mxc_isi_async_subdev
*masd
= asd_to_mxc_isi_async_subdev(asc
);
57 struct media_pad
*pad
= &isi
->crossbar
.pads
[masd
->port
];
58 struct device_link
*link
;
60 dev_dbg(isi
->dev
, "Bound subdev %s to crossbar input %u\n", sd
->name
,
64 * Enforce suspend/resume ordering between the source (supplier) and
65 * the ISI (consumer). The source will be suspended before and resume
68 link
= device_link_add(isi
->dev
, sd
->dev
, DL_FLAG_STATELESS
);
71 "Failed to create device link to source %s\n", sd
->name
);
75 return v4l2_create_fwnode_links_to_pad(sd
, pad
, link_flags
);
78 static int mxc_isi_async_notifier_complete(struct v4l2_async_notifier
*notifier
)
80 struct mxc_isi_dev
*isi
= notifier_to_mxc_isi_dev(notifier
);
83 dev_dbg(isi
->dev
, "All subdevs bound\n");
85 ret
= v4l2_device_register_subdev_nodes(&isi
->v4l2_dev
);
88 "Failed to register subdev nodes: %d\n", ret
);
92 return media_device_register(&isi
->media_dev
);
95 static const struct v4l2_async_notifier_operations mxc_isi_async_notifier_ops
= {
96 .bound
= mxc_isi_async_notifier_bound
,
97 .complete
= mxc_isi_async_notifier_complete
,
100 static int mxc_isi_pipe_register(struct mxc_isi_pipe
*pipe
)
104 ret
= v4l2_device_register_subdev(&pipe
->isi
->v4l2_dev
, &pipe
->sd
);
108 return mxc_isi_video_register(pipe
, &pipe
->isi
->v4l2_dev
);
111 static void mxc_isi_pipe_unregister(struct mxc_isi_pipe
*pipe
)
113 mxc_isi_video_unregister(pipe
);
116 static int mxc_isi_v4l2_init(struct mxc_isi_dev
*isi
)
118 struct fwnode_handle
*node
= dev_fwnode(isi
->dev
);
119 struct media_device
*media_dev
= &isi
->media_dev
;
120 struct v4l2_device
*v4l2_dev
= &isi
->v4l2_dev
;
124 /* Initialize the media device. */
125 strscpy(media_dev
->model
, "FSL Capture Media Device",
126 sizeof(media_dev
->model
));
127 media_dev
->dev
= isi
->dev
;
129 media_device_init(media_dev
);
131 /* Initialize and register the V4L2 device. */
132 v4l2_dev
->mdev
= media_dev
;
133 strscpy(v4l2_dev
->name
, "mx8-img-md", sizeof(v4l2_dev
->name
));
135 ret
= v4l2_device_register(isi
->dev
, v4l2_dev
);
138 "Failed to register V4L2 device: %d\n", ret
);
142 /* Register the crossbar switch subdev. */
143 ret
= mxc_isi_crossbar_register(&isi
->crossbar
);
145 dev_err(isi
->dev
, "Failed to register crossbar: %d\n", ret
);
149 /* Register the pipeline subdevs and link them to the crossbar switch. */
150 for (i
= 0; i
< isi
->pdata
->num_channels
; ++i
) {
151 struct mxc_isi_pipe
*pipe
= &isi
->pipes
[i
];
153 ret
= mxc_isi_pipe_register(pipe
);
155 dev_err(isi
->dev
, "Failed to register pipe%u: %d\n", i
,
160 ret
= media_create_pad_link(&isi
->crossbar
.sd
.entity
,
161 isi
->crossbar
.num_sinks
+ i
,
163 MXC_ISI_PIPE_PAD_SINK
,
164 MEDIA_LNK_FL_IMMUTABLE
|
165 MEDIA_LNK_FL_ENABLED
);
170 /* Register the M2M device. */
171 ret
= mxc_isi_m2m_register(isi
, v4l2_dev
);
173 dev_err(isi
->dev
, "Failed to register M2M device: %d\n", ret
);
177 /* Initialize, fill and register the async notifier. */
178 v4l2_async_nf_init(&isi
->notifier
, v4l2_dev
);
179 isi
->notifier
.ops
= &mxc_isi_async_notifier_ops
;
181 for (i
= 0; i
< isi
->pdata
->num_ports
; ++i
) {
182 struct mxc_isi_async_subdev
*masd
;
183 struct fwnode_handle
*ep
;
185 ep
= fwnode_graph_get_endpoint_by_id(node
, i
, 0,
186 FWNODE_GRAPH_ENDPOINT_NEXT
);
191 masd
= v4l2_async_nf_add_fwnode_remote(&isi
->notifier
, ep
,
192 struct mxc_isi_async_subdev
);
193 fwnode_handle_put(ep
);
203 ret
= v4l2_async_nf_register(&isi
->notifier
);
206 "Failed to register async notifier: %d\n", ret
);
213 mxc_isi_m2m_unregister(isi
);
214 v4l2_async_nf_cleanup(&isi
->notifier
);
216 v4l2_device_unregister(v4l2_dev
);
218 media_device_cleanup(media_dev
);
222 static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev
*isi
)
226 v4l2_async_nf_unregister(&isi
->notifier
);
227 v4l2_async_nf_cleanup(&isi
->notifier
);
229 v4l2_device_unregister(&isi
->v4l2_dev
);
230 media_device_unregister(&isi
->media_dev
);
232 mxc_isi_m2m_unregister(isi
);
234 for (i
= 0; i
< isi
->pdata
->num_channels
; ++i
)
235 mxc_isi_pipe_unregister(&isi
->pipes
[i
]);
237 mxc_isi_crossbar_unregister(&isi
->crossbar
);
239 media_device_cleanup(&isi
->media_dev
);
242 /* -----------------------------------------------------------------------------
246 /* Panic will assert when the buffers are 50% full */
248 /* For i.MX8QXP C0 and i.MX8MN ISI IER version */
249 static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v1
= {
250 .oflw_y_buf_en
= { .offset
= 19, .mask
= 0x80000 },
251 .oflw_u_buf_en
= { .offset
= 21, .mask
= 0x200000 },
252 .oflw_v_buf_en
= { .offset
= 23, .mask
= 0x800000 },
254 .panic_y_buf_en
= {.offset
= 20, .mask
= 0x100000 },
255 .panic_u_buf_en
= {.offset
= 22, .mask
= 0x400000 },
256 .panic_v_buf_en
= {.offset
= 24, .mask
= 0x1000000 },
259 /* For i.MX8MP ISI IER version */
260 static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v2
= {
261 .oflw_y_buf_en
= { .offset
= 18, .mask
= 0x40000 },
262 .oflw_u_buf_en
= { .offset
= 20, .mask
= 0x100000 },
263 .oflw_v_buf_en
= { .offset
= 22, .mask
= 0x400000 },
265 .panic_y_buf_en
= {.offset
= 19, .mask
= 0x80000 },
266 .panic_u_buf_en
= {.offset
= 21, .mask
= 0x200000 },
267 .panic_v_buf_en
= {.offset
= 23, .mask
= 0x800000 },
270 /* Panic will assert when the buffers are 50% full */
271 static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1
= {
272 .panic_set_thd_y
= { .mask
= 0x0000f, .offset
= 0, .threshold
= 0x7 },
273 .panic_set_thd_u
= { .mask
= 0x00f00, .offset
= 8, .threshold
= 0x7 },
274 .panic_set_thd_v
= { .mask
= 0xf0000, .offset
= 16, .threshold
= 0x7 },
277 static const struct clk_bulk_data mxc_imx8mn_clks
[] = {
282 static const struct mxc_isi_plat_data mxc_imx8mn_data
= {
283 .model
= MXC_ISI_IMX8MN
,
287 .ier_reg
= &mxc_imx8_isi_ier_v1
,
288 .set_thd
= &mxc_imx8_isi_thd_v1
,
289 .clks
= mxc_imx8mn_clks
,
290 .num_clks
= ARRAY_SIZE(mxc_imx8mn_clks
),
291 .buf_active_reverse
= false,
292 .gasket_ops
= &mxc_imx8_gasket_ops
,
293 .has_36bit_dma
= false,
296 static const struct mxc_isi_plat_data mxc_imx8mp_data
= {
297 .model
= MXC_ISI_IMX8MP
,
300 .reg_offset
= 0x2000,
301 .ier_reg
= &mxc_imx8_isi_ier_v2
,
302 .set_thd
= &mxc_imx8_isi_thd_v1
,
303 .clks
= mxc_imx8mn_clks
,
304 .num_clks
= ARRAY_SIZE(mxc_imx8mn_clks
),
305 .buf_active_reverse
= true,
306 .gasket_ops
= &mxc_imx8_gasket_ops
,
307 .has_36bit_dma
= true,
310 static const struct mxc_isi_plat_data mxc_imx93_data
= {
311 .model
= MXC_ISI_IMX93
,
315 .ier_reg
= &mxc_imx8_isi_ier_v2
,
316 .set_thd
= &mxc_imx8_isi_thd_v1
,
317 .clks
= mxc_imx8mn_clks
,
318 .num_clks
= ARRAY_SIZE(mxc_imx8mn_clks
),
319 .buf_active_reverse
= true,
320 .gasket_ops
= &mxc_imx93_gasket_ops
,
321 .has_36bit_dma
= false,
324 /* -----------------------------------------------------------------------------
328 static int mxc_isi_pm_suspend(struct device
*dev
)
330 struct mxc_isi_dev
*isi
= dev_get_drvdata(dev
);
333 for (i
= 0; i
< isi
->pdata
->num_channels
; ++i
) {
334 struct mxc_isi_pipe
*pipe
= &isi
->pipes
[i
];
336 mxc_isi_video_suspend(pipe
);
339 return pm_runtime_force_suspend(dev
);
342 static int mxc_isi_pm_resume(struct device
*dev
)
344 struct mxc_isi_dev
*isi
= dev_get_drvdata(dev
);
349 ret
= pm_runtime_force_resume(dev
);
353 for (i
= 0; i
< isi
->pdata
->num_channels
; ++i
) {
354 struct mxc_isi_pipe
*pipe
= &isi
->pipes
[i
];
356 ret
= mxc_isi_video_resume(pipe
);
358 dev_err(dev
, "Failed to resume pipeline %u (%d)\n", i
,
361 * Record the last error as it's as meaningful as any,
362 * and continue resuming the other pipelines.
371 static int mxc_isi_runtime_suspend(struct device
*dev
)
373 struct mxc_isi_dev
*isi
= dev_get_drvdata(dev
);
375 clk_bulk_disable_unprepare(isi
->pdata
->num_clks
, isi
->clks
);
380 static int mxc_isi_runtime_resume(struct device
*dev
)
382 struct mxc_isi_dev
*isi
= dev_get_drvdata(dev
);
385 ret
= clk_bulk_prepare_enable(isi
->pdata
->num_clks
, isi
->clks
);
387 dev_err(dev
, "Failed to enable clocks (%d)\n", ret
);
394 static const struct dev_pm_ops mxc_isi_pm_ops
= {
395 SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend
, mxc_isi_pm_resume
)
396 RUNTIME_PM_OPS(mxc_isi_runtime_suspend
, mxc_isi_runtime_resume
, NULL
)
399 /* -----------------------------------------------------------------------------
400 * Probe, remove & driver
403 static int mxc_isi_clk_get(struct mxc_isi_dev
*isi
)
405 unsigned int size
= isi
->pdata
->num_clks
406 * sizeof(*isi
->clks
);
409 isi
->clks
= devm_kmemdup(isi
->dev
, isi
->pdata
->clks
, size
, GFP_KERNEL
);
413 ret
= devm_clk_bulk_get(isi
->dev
, isi
->pdata
->num_clks
,
416 dev_err(isi
->dev
, "Failed to acquire clocks: %d\n",
424 static int mxc_isi_probe(struct platform_device
*pdev
)
426 struct device
*dev
= &pdev
->dev
;
427 struct mxc_isi_dev
*isi
;
428 unsigned int dma_size
;
432 isi
= devm_kzalloc(dev
, sizeof(*isi
), GFP_KERNEL
);
437 platform_set_drvdata(pdev
, isi
);
439 isi
->pdata
= of_device_get_match_data(dev
);
441 isi
->pipes
= kcalloc(isi
->pdata
->num_channels
, sizeof(isi
->pipes
[0]),
446 ret
= mxc_isi_clk_get(isi
);
448 dev_err(dev
, "Failed to get clocks\n");
452 isi
->regs
= devm_platform_ioremap_resource(pdev
, 0);
453 if (IS_ERR(isi
->regs
)) {
454 dev_err(dev
, "Failed to get ISI register map\n");
455 return PTR_ERR(isi
->regs
);
458 if (isi
->pdata
->gasket_ops
) {
459 isi
->gasket
= syscon_regmap_lookup_by_phandle(dev
->of_node
,
461 if (IS_ERR(isi
->gasket
)) {
462 ret
= PTR_ERR(isi
->gasket
);
463 dev_err(dev
, "failed to get gasket: %d\n", ret
);
468 dma_size
= isi
->pdata
->has_36bit_dma
? 36 : 32;
469 ret
= dma_set_mask_and_coherent(dev
, DMA_BIT_MASK(dma_size
));
471 dev_err(dev
, "failed to set DMA mask\n");
475 pm_runtime_enable(dev
);
477 ret
= mxc_isi_crossbar_init(isi
);
479 dev_err(dev
, "Failed to initialize crossbar: %d\n", ret
);
483 for (i
= 0; i
< isi
->pdata
->num_channels
; ++i
) {
484 ret
= mxc_isi_pipe_init(isi
, i
);
486 dev_err(dev
, "Failed to initialize pipe%u: %d\n", i
,
492 ret
= mxc_isi_v4l2_init(isi
);
494 dev_err(dev
, "Failed to initialize V4L2: %d\n", ret
);
498 mxc_isi_debug_init(isi
);
503 mxc_isi_crossbar_cleanup(&isi
->crossbar
);
505 pm_runtime_disable(isi
->dev
);
509 static void mxc_isi_remove(struct platform_device
*pdev
)
511 struct mxc_isi_dev
*isi
= platform_get_drvdata(pdev
);
514 mxc_isi_debug_cleanup(isi
);
516 for (i
= 0; i
< isi
->pdata
->num_channels
; ++i
) {
517 struct mxc_isi_pipe
*pipe
= &isi
->pipes
[i
];
519 mxc_isi_pipe_cleanup(pipe
);
522 mxc_isi_crossbar_cleanup(&isi
->crossbar
);
523 mxc_isi_v4l2_cleanup(isi
);
525 pm_runtime_disable(isi
->dev
);
528 static const struct of_device_id mxc_isi_of_match
[] = {
529 { .compatible
= "fsl,imx8mn-isi", .data
= &mxc_imx8mn_data
},
530 { .compatible
= "fsl,imx8mp-isi", .data
= &mxc_imx8mp_data
},
531 { .compatible
= "fsl,imx93-isi", .data
= &mxc_imx93_data
},
534 MODULE_DEVICE_TABLE(of
, mxc_isi_of_match
);
536 static struct platform_driver mxc_isi_driver
= {
537 .probe
= mxc_isi_probe
,
538 .remove_new
= mxc_isi_remove
,
540 .of_match_table
= mxc_isi_of_match
,
541 .name
= MXC_ISI_DRIVER_NAME
,
542 .pm
= pm_ptr(&mxc_isi_pm_ops
),
545 module_platform_driver(mxc_isi_driver
);
548 MODULE_AUTHOR("Freescale Semiconductor, Inc.");
549 MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver");
550 MODULE_LICENSE("GPL");