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.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_connection 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_connection *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_connection *asc) 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(asc); 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, v4l2_dev); 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(&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 .gasket_ops = &mxc_imx8_gasket_ops, 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 .gasket_ops = &mxc_imx8_gasket_ops, 307 .has_36bit_dma = true, 308 }; 309 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, 322 }; 323 324 /* ----------------------------------------------------------------------------- 325 * Power management 326 */ 327 328 static int mxc_isi_pm_suspend(struct device *dev) 329 { 330 struct mxc_isi_dev *isi = dev_get_drvdata(dev); 331 unsigned int i; 332 333 for (i = 0; i < isi->pdata->num_channels; ++i) { 334 struct mxc_isi_pipe *pipe = &isi->pipes[i]; 335 336 mxc_isi_video_suspend(pipe); 337 } 338 339 return pm_runtime_force_suspend(dev); 340 } 341 342 static int mxc_isi_pm_resume(struct device *dev) 343 { 344 struct mxc_isi_dev *isi = dev_get_drvdata(dev); 345 unsigned int i; 346 int err = 0; 347 int ret; 348 349 ret = pm_runtime_force_resume(dev); 350 if (ret < 0) 351 return ret; 352 353 for (i = 0; i < isi->pdata->num_channels; ++i) { 354 struct mxc_isi_pipe *pipe = &isi->pipes[i]; 355 356 ret = mxc_isi_video_resume(pipe); 357 if (ret) { 358 dev_err(dev, "Failed to resume pipeline %u (%d)\n", i, 359 ret); 360 /* 361 * Record the last error as it's as meaningful as any, 362 * and continue resuming the other pipelines. 363 */ 364 err = ret; 365 } 366 } 367 368 return err; 369 } 370 371 static int mxc_isi_runtime_suspend(struct device *dev) 372 { 373 struct mxc_isi_dev *isi = dev_get_drvdata(dev); 374 375 clk_bulk_disable_unprepare(isi->pdata->num_clks, isi->clks); 376 377 return 0; 378 } 379 380 static int mxc_isi_runtime_resume(struct device *dev) 381 { 382 struct mxc_isi_dev *isi = dev_get_drvdata(dev); 383 int ret; 384 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; 389 } 390 391 return 0; 392 } 393 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) 397 }; 398 399 /* ----------------------------------------------------------------------------- 400 * Probe, remove & driver 401 */ 402 403 static int mxc_isi_clk_get(struct mxc_isi_dev *isi) 404 { 405 unsigned int size = isi->pdata->num_clks 406 * sizeof(*isi->clks); 407 int ret; 408 409 isi->clks = devm_kmalloc(isi->dev, size, GFP_KERNEL); 410 if (!isi->clks) 411 return -ENOMEM; 412 413 memcpy(isi->clks, isi->pdata->clks, size); 414 415 ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks, 416 isi->clks); 417 if (ret < 0) { 418 dev_err(isi->dev, "Failed to acquire clocks: %d\n", 419 ret); 420 return ret; 421 } 422 423 return 0; 424 } 425 426 static int mxc_isi_probe(struct platform_device *pdev) 427 { 428 struct device *dev = &pdev->dev; 429 struct mxc_isi_dev *isi; 430 unsigned int dma_size; 431 unsigned int i; 432 int ret = 0; 433 434 isi = devm_kzalloc(dev, sizeof(*isi), GFP_KERNEL); 435 if (!isi) 436 return -ENOMEM; 437 438 isi->dev = dev; 439 platform_set_drvdata(pdev, isi); 440 441 isi->pdata = of_device_get_match_data(dev); 442 443 isi->pipes = kcalloc(isi->pdata->num_channels, sizeof(isi->pipes[0]), 444 GFP_KERNEL); 445 if (!isi->pipes) 446 return -ENOMEM; 447 448 ret = mxc_isi_clk_get(isi); 449 if (ret < 0) { 450 dev_err(dev, "Failed to get clocks\n"); 451 return ret; 452 } 453 454 isi->regs = devm_platform_ioremap_resource(pdev, 0); 455 if (IS_ERR(isi->regs)) { 456 dev_err(dev, "Failed to get ISI register map\n"); 457 return PTR_ERR(isi->regs); 458 } 459 460 if (isi->pdata->gasket_ops) { 461 isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node, 462 "fsl,blk-ctrl"); 463 if (IS_ERR(isi->gasket)) { 464 ret = PTR_ERR(isi->gasket); 465 dev_err(dev, "failed to get gasket: %d\n", ret); 466 return ret; 467 } 468 } 469 470 dma_size = isi->pdata->has_36bit_dma ? 36 : 32; 471 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); 472 if (ret) { 473 dev_err(dev, "failed to set DMA mask\n"); 474 return ret; 475 } 476 477 pm_runtime_enable(dev); 478 479 ret = mxc_isi_crossbar_init(isi); 480 if (ret) { 481 dev_err(dev, "Failed to initialize crossbar: %d\n", ret); 482 goto err_pm; 483 } 484 485 for (i = 0; i < isi->pdata->num_channels; ++i) { 486 ret = mxc_isi_pipe_init(isi, i); 487 if (ret < 0) { 488 dev_err(dev, "Failed to initialize pipe%u: %d\n", i, 489 ret); 490 goto err_xbar; 491 } 492 } 493 494 ret = mxc_isi_v4l2_init(isi); 495 if (ret < 0) { 496 dev_err(dev, "Failed to initialize V4L2: %d\n", ret); 497 goto err_xbar; 498 } 499 500 mxc_isi_debug_init(isi); 501 502 return 0; 503 504 err_xbar: 505 mxc_isi_crossbar_cleanup(&isi->crossbar); 506 err_pm: 507 pm_runtime_disable(isi->dev); 508 return ret; 509 } 510 511 static int mxc_isi_remove(struct platform_device *pdev) 512 { 513 struct mxc_isi_dev *isi = platform_get_drvdata(pdev); 514 unsigned int i; 515 516 mxc_isi_debug_cleanup(isi); 517 518 for (i = 0; i < isi->pdata->num_channels; ++i) { 519 struct mxc_isi_pipe *pipe = &isi->pipes[i]; 520 521 mxc_isi_pipe_cleanup(pipe); 522 } 523 524 mxc_isi_crossbar_cleanup(&isi->crossbar); 525 mxc_isi_v4l2_cleanup(isi); 526 527 pm_runtime_disable(isi->dev); 528 529 return 0; 530 } 531 532 static const struct of_device_id mxc_isi_of_match[] = { 533 { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data }, 534 { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data }, 535 { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, 536 { /* sentinel */ }, 537 }; 538 MODULE_DEVICE_TABLE(of, mxc_isi_of_match); 539 540 static struct platform_driver mxc_isi_driver = { 541 .probe = mxc_isi_probe, 542 .remove = mxc_isi_remove, 543 .driver = { 544 .of_match_table = mxc_isi_of_match, 545 .name = MXC_ISI_DRIVER_NAME, 546 .pm = pm_ptr(&mxc_isi_pm_ops), 547 } 548 }; 549 module_platform_driver(mxc_isi_driver); 550 551 MODULE_ALIAS("ISI"); 552 MODULE_AUTHOR("Freescale Semiconductor, Inc."); 553 MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver"); 554 MODULE_LICENSE("GPL"); 555