This driver registers a single, SoC wide media device, which all entities
in the media graph can be registered to. It is probed from device tree
using a capture-subsystem node, listing the IPUv3 input ports, similarly
to the already existing display-subsystem node that lists the IPUv3 output
ports.

Signed-off-by: Philipp Zabel <p.za...@pengutronix.de>
---
 .../devicetree/bindings/media/fsl-imx-capture.txt  |  25 +++
 drivers/media/platform/Kconfig                     |   2 +
 drivers/media/platform/Makefile                    |   1 +
 drivers/media/platform/imx/Kconfig                 |   7 +
 drivers/media/platform/imx/Makefile                |   1 +
 drivers/media/platform/imx/imx-media.c             | 249 +++++++++++++++++++++
 6 files changed, 285 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/fsl-imx-capture.txt
 create mode 100644 drivers/media/platform/imx/Kconfig
 create mode 100644 drivers/media/platform/imx/Makefile
 create mode 100644 drivers/media/platform/imx/imx-media.c

diff --git a/Documentation/devicetree/bindings/media/fsl-imx-capture.txt 
b/Documentation/devicetree/bindings/media/fsl-imx-capture.txt
new file mode 100644
index 0000000..5805331
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/fsl-imx-capture.txt
@@ -0,0 +1,25 @@
+Freescale i.MX IPUv3 capture subsystem
+======================================
+
+The i.MX5/6 IPUv3 capture subsystem consists of one or two IPUs and all
+external subdevices connected to the IPU CSI input ports. On i.MX5 these
+are only external subdevices. On i.MX6, there are additional SoC internal
+multiplexers and a MIPI CSI-2 bridge connected to the CSI input ports via
+of_graph bindings.
+
+On i.MX6 variants with two IPUs, either a single capture subsystem node may be
+defined, containing all CSI ports, or two separate capture subsystem nodes may
+be defined, each containing the CSI ports of a single IPU. In the latter case
+there must be no external of_graph links between the two subsystems.
+
+Required properties:
+- compatible: should be "fsl,imx-capture-subsystem"
+- ports: should contain a list of phandles pointing to the capture interface
+  ports of IPU devices
+
+Example:
+
+capture-subsystem {
+        compatible = "fsl,imx-capture-subsystem";
+        ports = <&ipu1_csi0>, <&ipu1_csi1>, <&ipu2_csi0>, <&ipu2_csi1>;
+};
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ce4a96f..105bf57 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -29,6 +29,8 @@ config VIDEO_VIA_CAMERA
 
 source "drivers/media/platform/davinci/Kconfig"
 
+source "drivers/media/platform/imx/Kconfig"
+
 source "drivers/media/platform/omap/Kconfig"
 
 source "drivers/media/platform/blackfin/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 40b18d1..f7f9008 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_VIDEO_RENESAS_FCP)       += rcar-fcp.o
 obj-$(CONFIG_VIDEO_RENESAS_JPU)        += rcar_jpu.o
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)       += vsp1/
 
+obj-y  += imx/
 obj-y  += omap/
 
 obj-$(CONFIG_VIDEO_AM437X_VPFE)                += am437x/
diff --git a/drivers/media/platform/imx/Kconfig 
b/drivers/media/platform/imx/Kconfig
new file mode 100644
index 0000000..3bd699c
--- /dev/null
+++ b/drivers/media/platform/imx/Kconfig
@@ -0,0 +1,7 @@
+config MEDIA_IMX
+       tristate "Multimedia Support for Freescale i.MX"
+       depends on MEDIA_CONTROLLER
+       default y if IMX_IPUV3_CORE
+       ---help---
+         This driver provides a SoC wide media controller device that all
+         multimedia components in i.MX5 and i.MX6 SoCs can register with.
diff --git a/drivers/media/platform/imx/Makefile 
b/drivers/media/platform/imx/Makefile
new file mode 100644
index 0000000..74bed76
--- /dev/null
+++ b/drivers/media/platform/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MEDIA_IMX)                        += imx-media.o
diff --git a/drivers/media/platform/imx/imx-media.c 
b/drivers/media/platform/imx/imx-media.c
new file mode 100644
index 0000000..1f1fab4
--- /dev/null
+++ b/drivers/media/platform/imx/imx-media.c
@@ -0,0 +1,249 @@
+/*
+ * i.MX V4L2 Capture Driver
+ *
+ * Copyright (C) 2016, Pengutronix, Philipp Zabel <ker...@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+#define IMX_MEDIA_MAX_PORTS    4
+
+struct imx_media {
+       struct media_device mdev;
+       struct v4l2_device v4l2_dev;
+       struct v4l2_async_notifier subdev_notifier;
+       struct v4l2_async_subdev subdevs[IMX_MEDIA_MAX_PORTS];
+};
+
+static int v4l2_of_create_pad_link(struct v4l2_subdev *sd,
+                                  struct v4l2_of_link *link)
+{
+       struct v4l2_subdev *remote_sd, *src_sd, *sink_sd;
+       struct media_link *mlink;
+       int src_port, sink_port;
+
+       if (link->local_port >= sd->entity.num_pads)
+               return -EINVAL;
+
+       remote_sd = v4l2_find_subdev_by_node(sd->v4l2_dev, link->remote_node);
+       if (!remote_sd)
+               return 0;
+
+       if (sd->entity.pads[link->local_port].flags & MEDIA_PAD_FL_SINK) {
+               src_sd = remote_sd;
+               src_port = link->remote_port;
+               sink_sd = sd;
+               sink_port = link->local_port;
+       } else {
+               src_sd = sd;
+               src_port = link->local_port;
+               sink_sd = remote_sd;
+               sink_port = link->remote_port;
+       }
+
+       mlink = media_entity_find_link(&src_sd->entity.pads[src_port],
+                                      &sink_sd->entity.pads[sink_port]);
+       if (mlink)
+               return 0;
+
+       return media_create_pad_link(&src_sd->entity, src_port,
+                       &sink_sd->entity, sink_port, 0);
+}
+
+static int imx_media_complete(struct v4l2_async_notifier *notifier)
+{
+       struct imx_media *im = container_of(notifier, struct imx_media,
+                                           subdev_notifier);
+       struct media_device *mdev = &im->mdev;
+       struct v4l2_subdev *sd;
+       struct device_node *ep;
+
+       mutex_lock(&mdev->graph_mutex);
+
+       /*
+        * Link all subdevices
+        */
+       list_for_each_entry(sd, &notifier->done, async_list) {
+               struct v4l2_of_link link;
+               int ret;
+
+               /*
+                * The IPU port nodes 0 and 1 correspond to the CSI subdevices.
+                * We can't use for_each_endpoint_of_node here, which would
+                * iterate over all endpoints of the IPU, including output ones.
+                */
+               if (strcmp(sd->of_node->name, "port") == 0) {
+                       /* There should be one endpoint in the CSI port */
+                       ep = of_get_next_child(sd->of_node, NULL);
+                       if (!ep)
+                               continue;
+
+                       ret = v4l2_of_parse_link(ep, &link);
+                       if (ret) {
+                               of_node_put(ep);
+                               continue;
+                       }
+
+                       /*
+                        * The IPU port id in the device tree does not
+                        * correspond to the CSI pad id. Always connect
+                        * the source to the CSI input pad (pad 0).
+                        */
+                       link.local_port = 0;
+
+                       v4l2_of_create_pad_link(sd, &link);
+
+                       v4l2_of_put_link(&link);
+
+                       continue;
+               }
+
+               /*
+                * TODO: for now assume that all subdevices other than CSI have
+                * a 1:1 relationship between device_node and v4l2_subdev and
+                * between of_graph port number and media_entity pad index.
+                */
+               for_each_endpoint_of_node(sd->of_node, ep) {
+                       ret = v4l2_of_parse_link(ep, &link);
+                       if (ret)
+                               continue;
+
+                       v4l2_of_create_pad_link(sd, &link);
+
+                       v4l2_of_put_link(&link);
+               }
+       }
+
+       mutex_unlock(&mdev->graph_mutex);
+
+       return v4l2_device_register_subdev_nodes(&im->v4l2_dev);
+}
+
+static void imx_media_register_notifier(struct imx_media *im)
+{
+       struct v4l2_async_notifier *notifier = &im->subdev_notifier;
+       struct v4l2_async_subdev *asd = &im->subdevs[0];
+       struct device_node *port;
+       int i;
+
+       /*
+        * ports correspond to the CSI subdevices that terminate the media
+        * pipelines.
+        */
+       for (i = 0; i < IMX_MEDIA_MAX_PORTS; i++) {
+               port = of_parse_phandle(im->mdev.dev->of_node, "ports", i);
+               if (!port)
+                       break;
+
+               if (!of_device_is_available(port->parent)) {
+                       of_node_put(port);
+                       continue;
+               }
+
+               asd[i].match_type = V4L2_ASYNC_MATCH_OF;
+               asd[i].match.of.node = port;
+               of_node_put(port);
+       }
+       notifier->num_subdevs = i;
+       notifier->subdevs = devm_kcalloc(im->mdev.dev, i,
+                                        sizeof(*notifier->subdevs),
+                                        GFP_KERNEL);
+       for (i = 0; i < notifier->num_subdevs; i++)
+               notifier->subdevs[i] = &im->subdevs[i];
+
+       notifier->complete = imx_media_complete;
+
+       v4l2_async_notifier_register(&im->v4l2_dev, notifier);
+}
+
+static int imx_media_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct media_device *mdev;
+       struct imx_media *im;
+       int ret;
+
+       ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+       if (ret)
+               return ret;
+
+       im = devm_kzalloc(dev, sizeof(*im), GFP_KERNEL);
+       if (!im)
+               return -ENOMEM;
+
+       mdev = &im->mdev;
+       mdev->dev = dev;
+       strlcpy(mdev->model, "i.MX IPUv3", sizeof(mdev->model));
+       media_device_init(mdev);
+
+       im->v4l2_dev.mdev = mdev;
+       ret = v4l2_device_register(dev, &im->v4l2_dev);
+       if (ret) {
+               dev_err(dev, "Failed to register v4l2 device: %d\n", ret);
+               return ret;
+       }
+
+       imx_media_register_notifier(im);
+
+       ret = media_device_register(mdev);
+       if (ret) {
+               dev_err(dev, "Failed to register media controller device: %d\n",
+                       ret);
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, mdev);
+
+       return 0;
+
+err:
+       media_device_cleanup(mdev);
+       return ret;
+}
+
+static int imx_media_remove(struct platform_device *pdev)
+{
+       struct media_device *mdev = platform_get_drvdata(pdev);
+       struct imx_media *im = container_of(mdev, struct imx_media, mdev);
+
+       media_device_unregister(mdev);
+       v4l2_device_unregister(&im->v4l2_dev);
+       media_device_cleanup(mdev);
+
+       return 0;
+}
+
+static const struct of_device_id imx_media_dt_ids[] = {
+       { .compatible = "fsl,imx-capture-subsystem", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, imx_media_dt_ids);
+
+static struct platform_driver imx_media_driver = {
+       .probe = imx_media_probe,
+       .remove = imx_media_remove,
+       .driver = {
+               .name = "imx-media",
+               .of_match_table = imx_media_dt_ids,
+       },
+};
+module_platform_driver(imx_media_driver);
+
+MODULE_AUTHOR("Sascha Hauer <s.ha...@pengutronix.de>");
+MODULE_AUTHOR("Philipp Zabel <p.za...@pengutronix.de>");
+MODULE_DESCRIPTION("i.MX SoC wide media device");
+MODULE_LICENSE("GPL");
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to