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 	SET_SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend, mxc_isi_pm_resume)
382 	SET_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		= &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");
540