.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * MIPI CSI-2 Receiver Subdev for Freescale i.MX6 SOC. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2012-2017 Mentor Graphics Inc. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | #include <linux/clk.h> |
---|
12 | 8 | #include <linux/interrupt.h> |
---|
.. | .. |
---|
18 | 14 | #include <linux/platform_device.h> |
---|
19 | 15 | #include <media/v4l2-device.h> |
---|
20 | 16 | #include <media/v4l2-fwnode.h> |
---|
| 17 | +#include <media/v4l2-mc.h> |
---|
21 | 18 | #include <media/v4l2-subdev.h> |
---|
22 | 19 | #include "imx-media.h" |
---|
23 | 20 | |
---|
.. | .. |
---|
39 | 36 | struct csi2_dev { |
---|
40 | 37 | struct device *dev; |
---|
41 | 38 | struct v4l2_subdev sd; |
---|
| 39 | + struct v4l2_async_notifier notifier; |
---|
42 | 40 | struct media_pad pad[CSI2_NUM_PADS]; |
---|
43 | 41 | struct clk *dphy_clk; |
---|
44 | 42 | struct clk *pllref_clk; |
---|
.. | .. |
---|
92 | 90 | static inline struct csi2_dev *sd_to_dev(struct v4l2_subdev *sdev) |
---|
93 | 91 | { |
---|
94 | 92 | return container_of(sdev, struct csi2_dev, sd); |
---|
| 93 | +} |
---|
| 94 | + |
---|
| 95 | +static inline struct csi2_dev *notifier_to_dev(struct v4l2_async_notifier *n) |
---|
| 96 | +{ |
---|
| 97 | + return container_of(n, struct csi2_dev, notifier); |
---|
95 | 98 | } |
---|
96 | 99 | |
---|
97 | 100 | /* |
---|
.. | .. |
---|
501 | 504 | return ret; |
---|
502 | 505 | } |
---|
503 | 506 | |
---|
504 | | -/* |
---|
505 | | - * retrieve our pads parsed from the OF graph by the media device |
---|
506 | | - */ |
---|
507 | 507 | static int csi2_registered(struct v4l2_subdev *sd) |
---|
508 | 508 | { |
---|
509 | 509 | struct csi2_dev *csi2 = sd_to_dev(sd); |
---|
510 | | - int i, ret; |
---|
511 | | - |
---|
512 | | - for (i = 0; i < CSI2_NUM_PADS; i++) { |
---|
513 | | - csi2->pad[i].flags = (i == CSI2_SINK_PAD) ? |
---|
514 | | - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; |
---|
515 | | - } |
---|
516 | 510 | |
---|
517 | 511 | /* set a default mbus format */ |
---|
518 | | - ret = imx_media_init_mbus_fmt(&csi2->format_mbus, |
---|
| 512 | + return imx_media_init_mbus_fmt(&csi2->format_mbus, |
---|
519 | 513 | 640, 480, 0, V4L2_FIELD_NONE, NULL); |
---|
520 | | - if (ret) |
---|
521 | | - return ret; |
---|
522 | | - |
---|
523 | | - return media_entity_pads_init(&sd->entity, CSI2_NUM_PADS, csi2->pad); |
---|
524 | 514 | } |
---|
525 | 515 | |
---|
526 | 516 | static const struct media_entity_operations csi2_entity_ops = { |
---|
527 | 517 | .link_setup = csi2_link_setup, |
---|
528 | 518 | .link_validate = v4l2_subdev_link_validate, |
---|
| 519 | + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, |
---|
529 | 520 | }; |
---|
530 | 521 | |
---|
531 | 522 | static const struct v4l2_subdev_video_ops csi2_video_ops = { |
---|
.. | .. |
---|
547 | 538 | .registered = csi2_registered, |
---|
548 | 539 | }; |
---|
549 | 540 | |
---|
550 | | -static int csi2_parse_endpoints(struct csi2_dev *csi2) |
---|
| 541 | +static int csi2_notify_bound(struct v4l2_async_notifier *notifier, |
---|
| 542 | + struct v4l2_subdev *sd, |
---|
| 543 | + struct v4l2_async_subdev *asd) |
---|
551 | 544 | { |
---|
552 | | - struct device_node *node = csi2->dev->of_node; |
---|
553 | | - struct device_node *epnode; |
---|
554 | | - struct v4l2_fwnode_endpoint ep; |
---|
| 545 | + struct csi2_dev *csi2 = notifier_to_dev(notifier); |
---|
| 546 | + struct media_pad *sink = &csi2->sd.entity.pads[CSI2_SINK_PAD]; |
---|
555 | 547 | |
---|
556 | | - epnode = of_graph_get_endpoint_by_regs(node, 0, -1); |
---|
557 | | - if (!epnode) { |
---|
558 | | - v4l2_err(&csi2->sd, "failed to get sink endpoint node\n"); |
---|
559 | | - return -EINVAL; |
---|
560 | | - } |
---|
| 548 | + return v4l2_create_fwnode_links_to_pad(sd, sink); |
---|
| 549 | +} |
---|
561 | 550 | |
---|
562 | | - v4l2_fwnode_endpoint_parse(of_fwnode_handle(epnode), &ep); |
---|
563 | | - of_node_put(epnode); |
---|
| 551 | +static const struct v4l2_async_notifier_operations csi2_notify_ops = { |
---|
| 552 | + .bound = csi2_notify_bound, |
---|
| 553 | +}; |
---|
564 | 554 | |
---|
565 | | - if (ep.bus_type != V4L2_MBUS_CSI2) { |
---|
566 | | - v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n"); |
---|
567 | | - return -EINVAL; |
---|
568 | | - } |
---|
| 555 | +static int csi2_async_register(struct csi2_dev *csi2) |
---|
| 556 | +{ |
---|
| 557 | + struct v4l2_fwnode_endpoint vep = { |
---|
| 558 | + .bus_type = V4L2_MBUS_CSI2_DPHY, |
---|
| 559 | + }; |
---|
| 560 | + struct v4l2_async_subdev *asd; |
---|
| 561 | + struct fwnode_handle *ep; |
---|
| 562 | + int ret; |
---|
569 | 563 | |
---|
570 | | - csi2->bus = ep.bus.mipi_csi2; |
---|
| 564 | + v4l2_async_notifier_init(&csi2->notifier); |
---|
| 565 | + |
---|
| 566 | + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi2->dev), 0, 0, |
---|
| 567 | + FWNODE_GRAPH_ENDPOINT_NEXT); |
---|
| 568 | + if (!ep) |
---|
| 569 | + return -ENOTCONN; |
---|
| 570 | + |
---|
| 571 | + ret = v4l2_fwnode_endpoint_parse(ep, &vep); |
---|
| 572 | + if (ret) |
---|
| 573 | + goto err_parse; |
---|
| 574 | + |
---|
| 575 | + csi2->bus = vep.bus.mipi_csi2; |
---|
571 | 576 | |
---|
572 | 577 | dev_dbg(csi2->dev, "data lanes: %d\n", csi2->bus.num_data_lanes); |
---|
573 | 578 | dev_dbg(csi2->dev, "flags: 0x%08x\n", csi2->bus.flags); |
---|
574 | | - return 0; |
---|
| 579 | + |
---|
| 580 | + asd = v4l2_async_notifier_add_fwnode_remote_subdev( |
---|
| 581 | + &csi2->notifier, ep, sizeof(*asd)); |
---|
| 582 | + fwnode_handle_put(ep); |
---|
| 583 | + |
---|
| 584 | + if (IS_ERR(asd)) |
---|
| 585 | + return PTR_ERR(asd); |
---|
| 586 | + |
---|
| 587 | + csi2->notifier.ops = &csi2_notify_ops; |
---|
| 588 | + |
---|
| 589 | + ret = v4l2_async_subdev_notifier_register(&csi2->sd, |
---|
| 590 | + &csi2->notifier); |
---|
| 591 | + if (ret) |
---|
| 592 | + return ret; |
---|
| 593 | + |
---|
| 594 | + return v4l2_async_register_subdev(&csi2->sd); |
---|
| 595 | + |
---|
| 596 | +err_parse: |
---|
| 597 | + fwnode_handle_put(ep); |
---|
| 598 | + return ret; |
---|
575 | 599 | } |
---|
576 | 600 | |
---|
577 | 601 | static int csi2_probe(struct platform_device *pdev) |
---|
578 | 602 | { |
---|
579 | 603 | struct csi2_dev *csi2; |
---|
580 | 604 | struct resource *res; |
---|
581 | | - int ret; |
---|
| 605 | + int i, ret; |
---|
582 | 606 | |
---|
583 | 607 | csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL); |
---|
584 | 608 | if (!csi2) |
---|
.. | .. |
---|
593 | 617 | csi2->sd.dev = &pdev->dev; |
---|
594 | 618 | csi2->sd.owner = THIS_MODULE; |
---|
595 | 619 | csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; |
---|
596 | | - strcpy(csi2->sd.name, DEVICE_NAME); |
---|
| 620 | + strscpy(csi2->sd.name, DEVICE_NAME, sizeof(csi2->sd.name)); |
---|
597 | 621 | csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; |
---|
598 | 622 | csi2->sd.grp_id = IMX_MEDIA_GRP_ID_CSI2; |
---|
599 | 623 | |
---|
600 | | - ret = csi2_parse_endpoints(csi2); |
---|
| 624 | + for (i = 0; i < CSI2_NUM_PADS; i++) { |
---|
| 625 | + csi2->pad[i].flags = (i == CSI2_SINK_PAD) ? |
---|
| 626 | + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; |
---|
| 627 | + } |
---|
| 628 | + |
---|
| 629 | + ret = media_entity_pads_init(&csi2->sd.entity, CSI2_NUM_PADS, |
---|
| 630 | + csi2->pad); |
---|
601 | 631 | if (ret) |
---|
602 | 632 | return ret; |
---|
603 | 633 | |
---|
604 | 634 | csi2->pllref_clk = devm_clk_get(&pdev->dev, "ref"); |
---|
605 | 635 | if (IS_ERR(csi2->pllref_clk)) { |
---|
606 | 636 | v4l2_err(&csi2->sd, "failed to get pll reference clock\n"); |
---|
607 | | - ret = PTR_ERR(csi2->pllref_clk); |
---|
608 | | - return ret; |
---|
| 637 | + return PTR_ERR(csi2->pllref_clk); |
---|
609 | 638 | } |
---|
610 | 639 | |
---|
611 | 640 | csi2->dphy_clk = devm_clk_get(&pdev->dev, "dphy"); |
---|
612 | 641 | if (IS_ERR(csi2->dphy_clk)) { |
---|
613 | 642 | v4l2_err(&csi2->sd, "failed to get dphy clock\n"); |
---|
614 | | - ret = PTR_ERR(csi2->dphy_clk); |
---|
615 | | - return ret; |
---|
| 643 | + return PTR_ERR(csi2->dphy_clk); |
---|
616 | 644 | } |
---|
617 | 645 | |
---|
618 | 646 | csi2->pix_clk = devm_clk_get(&pdev->dev, "pix"); |
---|
619 | 647 | if (IS_ERR(csi2->pix_clk)) { |
---|
620 | 648 | v4l2_err(&csi2->sd, "failed to get pixel clock\n"); |
---|
621 | | - ret = PTR_ERR(csi2->pix_clk); |
---|
622 | | - return ret; |
---|
| 649 | + return PTR_ERR(csi2->pix_clk); |
---|
623 | 650 | } |
---|
624 | 651 | |
---|
625 | 652 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
.. | .. |
---|
629 | 656 | } |
---|
630 | 657 | |
---|
631 | 658 | csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE); |
---|
632 | | - if (!csi2->base) { |
---|
633 | | - v4l2_err(&csi2->sd, "failed to map CSI-2 registers\n"); |
---|
| 659 | + if (!csi2->base) |
---|
634 | 660 | return -ENOMEM; |
---|
635 | | - } |
---|
636 | 661 | |
---|
637 | 662 | mutex_init(&csi2->lock); |
---|
638 | 663 | |
---|
.. | .. |
---|
650 | 675 | |
---|
651 | 676 | platform_set_drvdata(pdev, &csi2->sd); |
---|
652 | 677 | |
---|
653 | | - ret = v4l2_async_register_subdev(&csi2->sd); |
---|
| 678 | + ret = csi2_async_register(csi2); |
---|
654 | 679 | if (ret) |
---|
655 | | - goto dphy_off; |
---|
| 680 | + goto clean_notifier; |
---|
656 | 681 | |
---|
657 | 682 | return 0; |
---|
658 | 683 | |
---|
659 | | -dphy_off: |
---|
| 684 | +clean_notifier: |
---|
| 685 | + v4l2_async_notifier_unregister(&csi2->notifier); |
---|
| 686 | + v4l2_async_notifier_cleanup(&csi2->notifier); |
---|
660 | 687 | clk_disable_unprepare(csi2->dphy_clk); |
---|
661 | 688 | pllref_off: |
---|
662 | 689 | clk_disable_unprepare(csi2->pllref_clk); |
---|
.. | .. |
---|
670 | 697 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); |
---|
671 | 698 | struct csi2_dev *csi2 = sd_to_dev(sd); |
---|
672 | 699 | |
---|
| 700 | + v4l2_async_notifier_unregister(&csi2->notifier); |
---|
| 701 | + v4l2_async_notifier_cleanup(&csi2->notifier); |
---|
673 | 702 | v4l2_async_unregister_subdev(sd); |
---|
674 | 703 | clk_disable_unprepare(csi2->dphy_clk); |
---|
675 | 704 | clk_disable_unprepare(csi2->pllref_clk); |
---|