media: nxp: ignore unused suspend operations
[linux-block.git] / drivers / media / platform / nxp / imx8-isi / imx8-isi-core.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019-2020 NXP
4  */
5
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_device.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>
20
21 #include <media/media-device.h>
22 #include <media/v4l2-async.h>
23 #include <media/v4l2-device.h>
24 #include <media/v4l2-mc.h>
25
26 #include "imx8-isi-core.h"
27
28 /* -----------------------------------------------------------------------------
29  * V4L2 async subdevs
30  */
31
32 struct mxc_isi_async_subdev {
33         struct v4l2_async_subdev asd;
34         unsigned int port;
35 };
36
37 static inline struct mxc_isi_async_subdev *
38 asd_to_mxc_isi_async_subdev(struct v4l2_async_subdev *asd)
39 {
40         return container_of(asd, struct mxc_isi_async_subdev, asd);
41 };
42
43 static inline struct mxc_isi_dev *
44 notifier_to_mxc_isi_dev(struct v4l2_async_notifier *n)
45 {
46         return container_of(n, struct mxc_isi_dev, notifier);
47 };
48
49 static int mxc_isi_async_notifier_bound(struct v4l2_async_notifier *notifier,
50                                         struct v4l2_subdev *sd,
51                                         struct v4l2_async_subdev *asd)
52 {
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(asd);
57         struct media_pad *pad = &isi->crossbar.pads[masd->port];
58         struct device_link *link;
59
60         dev_dbg(isi->dev, "Bound subdev %s to crossbar input %u\n", sd->name,
61                 masd->port);
62
63         /*
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.
67          */
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;
73         }
74
75         return v4l2_create_fwnode_links_to_pad(sd, pad, link_flags);
76 }
77
78 static int mxc_isi_async_notifier_complete(struct v4l2_async_notifier *notifier)
79 {
80         struct mxc_isi_dev *isi = notifier_to_mxc_isi_dev(notifier);
81         int ret;
82
83         dev_dbg(isi->dev, "All subdevs bound\n");
84
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;
90         }
91
92         return media_device_register(&isi->media_dev);
93 }
94
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,
98 };
99
100 static int mxc_isi_pipe_register(struct mxc_isi_pipe *pipe)
101 {
102         int ret;
103
104         ret = v4l2_device_register_subdev(&pipe->isi->v4l2_dev, &pipe->sd);
105         if (ret < 0)
106                 return ret;
107
108         return mxc_isi_video_register(pipe, &pipe->isi->v4l2_dev);
109 }
110
111 static void mxc_isi_pipe_unregister(struct mxc_isi_pipe *pipe)
112 {
113         mxc_isi_video_unregister(pipe);
114 }
115
116 static int mxc_isi_v4l2_init(struct mxc_isi_dev *isi)
117 {
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;
123
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;
128
129         media_device_init(media_dev);
130
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));
134
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;
140         }
141
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;
147         }
148
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];
152
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;
158                 }
159
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;
168         }
169
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;
175         }
176
177         /* Initialize, fill and register the async notifier. */
178         v4l2_async_nf_init(&isi->notifier);
179         isi->notifier.ops = &mxc_isi_async_notifier_ops;
180
181         for (i = 0; i < isi->pdata->num_ports; ++i) {
182                 struct mxc_isi_async_subdev *masd;
183                 struct fwnode_handle *ep;
184
185                 ep = fwnode_graph_get_endpoint_by_id(node, i, 0,
186                                                      FWNODE_GRAPH_ENDPOINT_NEXT);
187
188                 if (!ep)
189                         continue;
190
191                 masd = v4l2_async_nf_add_fwnode_remote(&isi->notifier, ep,
192                                                        struct mxc_isi_async_subdev);
193                 fwnode_handle_put(ep);
194
195                 if (IS_ERR(masd)) {
196                         ret = PTR_ERR(masd);
197                         goto err_m2m;
198                 }
199
200                 masd->port = i;
201         }
202
203         ret = v4l2_async_nf_register(v4l2_dev, &isi->notifier);
204         if (ret < 0) {
205                 dev_err(isi->dev,
206                         "Failed to register async notifier: %d\n", ret);
207                 goto err_m2m;
208         }
209
210         return 0;
211
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;
220 }
221
222 static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi)
223 {
224         unsigned int i;
225
226         v4l2_async_nf_unregister(&isi->notifier);
227         v4l2_async_nf_cleanup(&isi->notifier);
228
229         v4l2_device_unregister(&isi->v4l2_dev);
230         media_device_unregister(&isi->media_dev);
231
232         mxc_isi_m2m_unregister(isi);
233
234         for (i = 0; i < isi->pdata->num_channels; ++i)
235                 mxc_isi_pipe_unregister(&isi->pipes[i]);
236
237         mxc_isi_crossbar_unregister(&isi->crossbar);
238
239         media_device_cleanup(&isi->media_dev);
240 }
241
242 /* -----------------------------------------------------------------------------
243  * Device information
244  */
245
246 /* Panic will assert when the buffers are 50% full */
247
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 },
253
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 },
257 };
258
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 },
264
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 },
268 };
269
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 },
275 };
276
277 static const struct clk_bulk_data mxc_imx8mn_clks[] = {
278         { .id = "axi" },
279         { .id = "apb" },
280 };
281
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         .has_gasket             = true,
293         .has_36bit_dma          = false,
294 };
295
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         .has_gasket             = true,
307         .has_36bit_dma          = true,
308 };
309
310 /* -----------------------------------------------------------------------------
311  * Power management
312  */
313
314 static int mxc_isi_pm_suspend(struct device *dev)
315 {
316         struct mxc_isi_dev *isi = dev_get_drvdata(dev);
317         unsigned int i;
318
319         for (i = 0; i < isi->pdata->num_channels; ++i) {
320                 struct mxc_isi_pipe *pipe = &isi->pipes[i];
321
322                 mxc_isi_video_suspend(pipe);
323         }
324
325         return pm_runtime_force_suspend(dev);
326 }
327
328 static int mxc_isi_pm_resume(struct device *dev)
329 {
330         struct mxc_isi_dev *isi = dev_get_drvdata(dev);
331         unsigned int i;
332         int err = 0;
333         int ret;
334
335         ret = pm_runtime_force_resume(dev);
336         if (ret < 0)
337                 return ret;
338
339         for (i = 0; i < isi->pdata->num_channels; ++i) {
340                 struct mxc_isi_pipe *pipe = &isi->pipes[i];
341
342                 ret = mxc_isi_video_resume(pipe);
343                 if (ret) {
344                         dev_err(dev, "Failed to resume pipeline %u (%d)\n", i,
345                                 ret);
346                         /*
347                          * Record the last error as it's as meaningful as any,
348                          * and continue resuming the other pipelines.
349                          */
350                         err = ret;
351                 }
352         }
353
354         return err;
355 }
356
357 static int mxc_isi_runtime_suspend(struct device *dev)
358 {
359         struct mxc_isi_dev *isi = dev_get_drvdata(dev);
360
361         clk_bulk_disable_unprepare(isi->pdata->num_clks, isi->clks);
362
363         return 0;
364 }
365
366 static int mxc_isi_runtime_resume(struct device *dev)
367 {
368         struct mxc_isi_dev *isi = dev_get_drvdata(dev);
369         int ret;
370
371         ret = clk_bulk_prepare_enable(isi->pdata->num_clks, isi->clks);
372         if (ret) {
373                 dev_err(dev, "Failed to enable clocks (%d)\n", ret);
374                 return ret;
375         }
376
377         return 0;
378 }
379
380 static const struct dev_pm_ops mxc_isi_pm_ops = {
381         SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend, mxc_isi_pm_resume)
382         RUNTIME_PM_OPS(mxc_isi_runtime_suspend, mxc_isi_runtime_resume, NULL)
383 };
384
385 /* -----------------------------------------------------------------------------
386  * Probe, remove & driver
387  */
388
389 static int mxc_isi_clk_get(struct mxc_isi_dev *isi)
390 {
391         unsigned int size = isi->pdata->num_clks
392                           * sizeof(*isi->clks);
393         int ret;
394
395         isi->clks = devm_kmalloc(isi->dev, size, GFP_KERNEL);
396         if (!isi->clks)
397                 return -ENOMEM;
398
399         memcpy(isi->clks, isi->pdata->clks, size);
400
401         ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks,
402                                 isi->clks);
403         if (ret < 0) {
404                 dev_err(isi->dev, "Failed to acquire clocks: %d\n",
405                         ret);
406                 return ret;
407         }
408
409         return 0;
410 }
411
412 static int mxc_isi_probe(struct platform_device *pdev)
413 {
414         struct device *dev = &pdev->dev;
415         struct mxc_isi_dev *isi;
416         unsigned int dma_size;
417         unsigned int i;
418         int ret = 0;
419
420         isi = devm_kzalloc(dev, sizeof(*isi), GFP_KERNEL);
421         if (!isi)
422                 return -ENOMEM;
423
424         isi->dev = dev;
425         platform_set_drvdata(pdev, isi);
426
427         isi->pdata = of_device_get_match_data(dev);
428
429         isi->pipes = kcalloc(isi->pdata->num_channels, sizeof(isi->pipes[0]),
430                              GFP_KERNEL);
431         if (!isi->pipes)
432                 return -ENOMEM;
433
434         ret = mxc_isi_clk_get(isi);
435         if (ret < 0) {
436                 dev_err(dev, "Failed to get clocks\n");
437                 return ret;
438         }
439
440         isi->regs = devm_platform_ioremap_resource(pdev, 0);
441         if (IS_ERR(isi->regs)) {
442                 dev_err(dev, "Failed to get ISI register map\n");
443                 return PTR_ERR(isi->regs);
444         }
445
446         if (isi->pdata->has_gasket) {
447                 isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node,
448                                                               "fsl,blk-ctrl");
449                 if (IS_ERR(isi->gasket)) {
450                         ret = PTR_ERR(isi->gasket);
451                         dev_err(dev, "failed to get gasket: %d\n", ret);
452                         return ret;
453                 }
454         }
455
456         dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
457         ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
458         if (ret) {
459                 dev_err(dev, "failed to set DMA mask\n");
460                 return ret;
461         }
462
463         pm_runtime_enable(dev);
464
465         ret = mxc_isi_crossbar_init(isi);
466         if (ret) {
467                 dev_err(dev, "Failed to initialize crossbar: %d\n", ret);
468                 goto err_pm;
469         }
470
471         for (i = 0; i < isi->pdata->num_channels; ++i) {
472                 ret = mxc_isi_pipe_init(isi, i);
473                 if (ret < 0) {
474                         dev_err(dev, "Failed to initialize pipe%u: %d\n", i,
475                                 ret);
476                         goto err_xbar;
477                 }
478         }
479
480         ret = mxc_isi_v4l2_init(isi);
481         if (ret < 0) {
482                 dev_err(dev, "Failed to initialize V4L2: %d\n", ret);
483                 goto err_xbar;
484         }
485
486         mxc_isi_debug_init(isi);
487
488         return 0;
489
490 err_xbar:
491         mxc_isi_crossbar_cleanup(&isi->crossbar);
492 err_pm:
493         pm_runtime_disable(isi->dev);
494         return ret;
495 }
496
497 static int mxc_isi_remove(struct platform_device *pdev)
498 {
499         struct mxc_isi_dev *isi = platform_get_drvdata(pdev);
500         unsigned int i;
501
502         mxc_isi_debug_cleanup(isi);
503
504         for (i = 0; i < isi->pdata->num_channels; ++i) {
505                 struct mxc_isi_pipe *pipe = &isi->pipes[i];
506
507                 mxc_isi_pipe_cleanup(pipe);
508         }
509
510         mxc_isi_crossbar_cleanup(&isi->crossbar);
511         mxc_isi_v4l2_cleanup(isi);
512
513         pm_runtime_disable(isi->dev);
514
515         return 0;
516 }
517
518 static const struct of_device_id mxc_isi_of_match[] = {
519         { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data },
520         { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data },
521         { /* sentinel */ },
522 };
523 MODULE_DEVICE_TABLE(of, mxc_isi_of_match);
524
525 static struct platform_driver mxc_isi_driver = {
526         .probe          = mxc_isi_probe,
527         .remove         = mxc_isi_remove,
528         .driver = {
529                 .of_match_table = mxc_isi_of_match,
530                 .name           = MXC_ISI_DRIVER_NAME,
531                 .pm             = pm_ptr(&mxc_isi_pm_ops),
532         }
533 };
534 module_platform_driver(mxc_isi_driver);
535
536 MODULE_ALIAS("ISI");
537 MODULE_AUTHOR("Freescale Semiconductor, Inc.");
538 MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver");
539 MODULE_LICENSE("GPL");