// SPDX-License-Identifier: GPL-2.0+ /* * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC * * Copyright (c) 2016-2019 Mentor Graphics Inc. */ #include #include #include #include #include #include #include "imx-media.h" static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) { return container_of(n, struct imx_media_dev, notifier); } /* async subdev bound notifier */ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret; if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) { /* register the IPU internal subdevs */ ret = imx_media_register_ipu_internal_subdevs(imxmd, sd); if (ret) return ret; } v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name); return 0; } /* async subdev complete notifier */ static int imx6_media_probe_complete(struct v4l2_async_notifier *notifier) { struct imx_media_dev *imxmd = notifier2dev(notifier); int ret; /* call the imx5/6/7 common probe completion handler */ ret = imx_media_probe_complete(notifier); if (ret) return ret; mutex_lock(&imxmd->mutex); imxmd->m2m_vdev = imx_media_csc_scaler_device_init(imxmd); if (IS_ERR(imxmd->m2m_vdev)) { ret = PTR_ERR(imxmd->m2m_vdev); goto unlock; } ret = imx_media_csc_scaler_device_register(imxmd->m2m_vdev); unlock: mutex_unlock(&imxmd->mutex); return ret; } /* async subdev complete notifier */ static const struct v4l2_async_notifier_operations imx_media_notifier_ops = { .bound = imx_media_subdev_bound, .complete = imx6_media_probe_complete, }; static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct imx_media_dev *imxmd; int ret; imxmd = imx_media_dev_init(dev, NULL); if (IS_ERR(imxmd)) return PTR_ERR(imxmd); ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_of_subdevs failed with %d\n", ret); goto cleanup; } ret = imx_media_dev_notifier_register(imxmd, &imx_media_notifier_ops); if (ret) goto cleanup; return 0; cleanup: v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); media_device_cleanup(&imxmd->md); return ret; } static int imx_media_remove(struct platform_device *pdev) { struct imx_media_dev *imxmd = (struct imx_media_dev *)platform_get_drvdata(pdev); v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); v4l2_async_notifier_unregister(&imxmd->notifier); imx_media_unregister_ipu_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); imx_media_csc_scaler_device_unregister(imxmd->m2m_vdev); media_device_unregister(&imxmd->md); v4l2_device_unregister(&imxmd->v4l2_dev); media_device_cleanup(&imxmd->md); return 0; } static const struct of_device_id imx_media_dt_ids[] = { { .compatible = "fsl,imx-capture-subsystem" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_media_dt_ids); static struct platform_driver imx_media_pdrv = { .probe = imx_media_probe, .remove = imx_media_remove, .driver = { .name = "imx-media", .of_match_table = imx_media_dt_ids, }, }; module_platform_driver(imx_media_pdrv); MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver"); MODULE_AUTHOR("Steve Longerbeam "); MODULE_LICENSE("GPL");