drm/tests: hdmi: Fix memory leaks in drm_display_mode_from_cea_vic()
[drm/drm-misc.git] / drivers / media / platform / nxp / imx8-isi / imx8-isi-core.c
blobc2013995049c603af2a57ddd326abc84450e5d95
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2019-2020 NXP
4 */
6 #include <linux/clk.h>
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>
12 #include <linux/of.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm.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 /* -----------------------------------------------------------------------------
29 * V4L2 async subdevs
32 struct mxc_isi_async_subdev {
33 struct v4l2_async_connection asd;
34 unsigned int port;
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,
61 masd->port);
64 * Enforce suspend/resume ordering between the source (supplier) and
65 * the ISI (consumer). The source will be suspended before and resume
66 * after the ISI.
68 link = device_link_add(isi->dev, sd->dev, DL_FLAG_STATELESS);
69 if (!link) {
70 dev_err(isi->dev,
71 "Failed to create device link to source %s\n", sd->name);
72 return -EINVAL;
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);
81 int ret;
83 dev_dbg(isi->dev, "All subdevs bound\n");
85 ret = v4l2_device_register_subdev_nodes(&isi->v4l2_dev);
86 if (ret < 0) {
87 dev_err(isi->dev,
88 "Failed to register subdev nodes: %d\n", ret);
89 return 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)
102 int ret;
104 ret = v4l2_device_register_subdev(&pipe->isi->v4l2_dev, &pipe->sd);
105 if (ret < 0)
106 return ret;
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;
121 unsigned int i;
122 int ret;
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);
136 if (ret < 0) {
137 dev_err(isi->dev,
138 "Failed to register V4L2 device: %d\n", ret);
139 goto err_media;
142 /* Register the crossbar switch subdev. */
143 ret = mxc_isi_crossbar_register(&isi->crossbar);
144 if (ret < 0) {
145 dev_err(isi->dev, "Failed to register crossbar: %d\n", ret);
146 goto err_v4l2;
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);
154 if (ret < 0) {
155 dev_err(isi->dev, "Failed to register pipe%u: %d\n", i,
156 ret);
157 goto err_v4l2;
160 ret = media_create_pad_link(&isi->crossbar.sd.entity,
161 isi->crossbar.num_sinks + i,
162 &pipe->sd.entity,
163 MXC_ISI_PIPE_PAD_SINK,
164 MEDIA_LNK_FL_IMMUTABLE |
165 MEDIA_LNK_FL_ENABLED);
166 if (ret < 0)
167 goto err_v4l2;
170 /* Register the M2M device. */
171 ret = mxc_isi_m2m_register(isi, v4l2_dev);
172 if (ret < 0) {
173 dev_err(isi->dev, "Failed to register M2M device: %d\n", ret);
174 goto err_v4l2;
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);
188 if (!ep)
189 continue;
191 masd = v4l2_async_nf_add_fwnode_remote(&isi->notifier, ep,
192 struct mxc_isi_async_subdev);
193 fwnode_handle_put(ep);
195 if (IS_ERR(masd)) {
196 ret = PTR_ERR(masd);
197 goto err_m2m;
200 masd->port = i;
203 ret = v4l2_async_nf_register(&isi->notifier);
204 if (ret < 0) {
205 dev_err(isi->dev,
206 "Failed to register async notifier: %d\n", ret);
207 goto err_m2m;
210 return 0;
212 err_m2m:
213 mxc_isi_m2m_unregister(isi);
214 v4l2_async_nf_cleanup(&isi->notifier);
215 err_v4l2:
216 v4l2_device_unregister(v4l2_dev);
217 err_media:
218 media_device_cleanup(media_dev);
219 return ret;
222 static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi)
224 unsigned int i;
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 /* -----------------------------------------------------------------------------
243 * Device information
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[] = {
278 { .id = "axi" },
279 { .id = "apb" },
282 static const struct mxc_isi_plat_data mxc_imx8mn_data = {
283 .model = MXC_ISI_IMX8MN,
284 .num_ports = 1,
285 .num_channels = 1,
286 .reg_offset = 0,
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,
298 .num_ports = 2,
299 .num_channels = 2,
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,
312 .num_ports = 1,
313 .num_channels = 1,
314 .reg_offset = 0,
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 /* -----------------------------------------------------------------------------
325 * Power management
328 static int mxc_isi_pm_suspend(struct device *dev)
330 struct mxc_isi_dev *isi = dev_get_drvdata(dev);
331 unsigned int i;
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);
345 unsigned int i;
346 int err = 0;
347 int ret;
349 ret = pm_runtime_force_resume(dev);
350 if (ret < 0)
351 return ret;
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);
357 if (ret) {
358 dev_err(dev, "Failed to resume pipeline %u (%d)\n", i,
359 ret);
361 * Record the last error as it's as meaningful as any,
362 * and continue resuming the other pipelines.
364 err = ret;
368 return err;
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);
377 return 0;
380 static int mxc_isi_runtime_resume(struct device *dev)
382 struct mxc_isi_dev *isi = dev_get_drvdata(dev);
383 int ret;
385 ret = clk_bulk_prepare_enable(isi->pdata->num_clks, isi->clks);
386 if (ret) {
387 dev_err(dev, "Failed to enable clocks (%d)\n", ret);
388 return ret;
391 return 0;
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);
407 int ret;
409 isi->clks = devm_kmemdup(isi->dev, isi->pdata->clks, size, GFP_KERNEL);
410 if (!isi->clks)
411 return -ENOMEM;
413 ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks,
414 isi->clks);
415 if (ret < 0) {
416 dev_err(isi->dev, "Failed to acquire clocks: %d\n",
417 ret);
418 return ret;
421 return 0;
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;
429 unsigned int i;
430 int ret = 0;
432 isi = devm_kzalloc(dev, sizeof(*isi), GFP_KERNEL);
433 if (!isi)
434 return -ENOMEM;
436 isi->dev = dev;
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]),
442 GFP_KERNEL);
443 if (!isi->pipes)
444 return -ENOMEM;
446 ret = mxc_isi_clk_get(isi);
447 if (ret < 0) {
448 dev_err(dev, "Failed to get clocks\n");
449 return ret;
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,
460 "fsl,blk-ctrl");
461 if (IS_ERR(isi->gasket)) {
462 ret = PTR_ERR(isi->gasket);
463 dev_err(dev, "failed to get gasket: %d\n", ret);
464 return ret;
468 dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
469 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
470 if (ret) {
471 dev_err(dev, "failed to set DMA mask\n");
472 return ret;
475 pm_runtime_enable(dev);
477 ret = mxc_isi_crossbar_init(isi);
478 if (ret) {
479 dev_err(dev, "Failed to initialize crossbar: %d\n", ret);
480 goto err_pm;
483 for (i = 0; i < isi->pdata->num_channels; ++i) {
484 ret = mxc_isi_pipe_init(isi, i);
485 if (ret < 0) {
486 dev_err(dev, "Failed to initialize pipe%u: %d\n", i,
487 ret);
488 goto err_xbar;
492 ret = mxc_isi_v4l2_init(isi);
493 if (ret < 0) {
494 dev_err(dev, "Failed to initialize V4L2: %d\n", ret);
495 goto err_xbar;
498 mxc_isi_debug_init(isi);
500 return 0;
502 err_xbar:
503 mxc_isi_crossbar_cleanup(&isi->crossbar);
504 err_pm:
505 pm_runtime_disable(isi->dev);
506 return ret;
509 static void mxc_isi_remove(struct platform_device *pdev)
511 struct mxc_isi_dev *isi = platform_get_drvdata(pdev);
512 unsigned int i;
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 },
532 { /* sentinel */ },
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,
539 .driver = {
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);
547 MODULE_ALIAS("ISI");
548 MODULE_AUTHOR("Freescale Semiconductor, Inc.");
549 MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver");
550 MODULE_LICENSE("GPL");