Contributors: 6
Author Tokens Token Proportion Commits Commit Proportion
Helen Mae Koike Fornazier 1756 79.49% 6 31.58%
Dafna Hirschfeld 246 11.14% 7 36.84%
Jacob Chen 153 6.93% 2 10.53%
Heiko Stübner 28 1.27% 1 5.26%
Ezequiel García 23 1.04% 2 10.53%
Laurent Pinchart 3 0.14% 1 5.26%
Total 2209 19


// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Rockchip ISP1 Driver - Base driver
 *
 * Copyright (C) 2019 Collabora, Ltd.
 *
 * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
 * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
 */

#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <media/v4l2-fwnode.h>

#include "rkisp1-common.h"

/*
 * ISP Details
 * -----------
 *
 * ISP Comprises with:
 *	MIPI serial camera interface
 *	Image Signal Processing
 *	Many Image Enhancement Blocks
 *	Crop
 *	Resizer
 *	RBG display ready image
 *	Image Rotation
 *
 * ISP Block Diagram
 * -----------------
 *                                                             rkisp1-resizer.c          rkisp1-capture.c
 *                                                          |====================|  |=======================|
 *                                rkisp1-isp.c                              Main Picture Path
 *                        |==========================|      |===============================================|
 *                        +-----------+  +--+--+--+--+      +--------+  +--------+              +-----------+
 *                        |           |  |  |  |  |  |      |        |  |        |              |           |
 * +--------+    |\       |           |  |  |  |  |  |   -->|  Crop  |->|  RSZ   |------------->|           |
 * |  MIPI  |--->|  \     |           |  |  |  |  |  |   |  |        |  |        |              |           |
 * +--------+    |   |    |           |  |IE|IE|IE|IE|   |  +--------+  +--------+              |  Memory   |
 *               |MUX|--->|    ISP    |->|0 |1 |2 |3 |---+                                      | Interface |
 * +--------+    |   |    |           |  |  |  |  |  |   |  +--------+  +--------+  +--------+  |           |
 * |Parallel|--->|  /     |           |  |  |  |  |  |   |  |        |  |        |  |        |  |           |
 * +--------+    |/       |           |  |  |  |  |  |   -->|  Crop  |->|  RSZ   |->|  RGB   |->|           |
 *                        |           |  |  |  |  |  |      |        |  |        |  | Rotate |  |           |
 *                        +-----------+  +--+--+--+--+      +--------+  +--------+  +--------+  +-----------+
 *                                               ^
 * +--------+                                    |          |===============================================|
 * |  DMA   |------------------------------------+                          Self Picture Path
 * +--------+
 *
 *         rkisp1-stats.c        rkisp1-params.c
 *       |===============|      |===============|
 *       +---------------+      +---------------+
 *       |               |      |               |
 *       |      ISP      |      |      ISP      |
 *       |               |      |               |
 *       +---------------+      +---------------+
 *
 *
 * Media Topology
 * --------------
 *      +----------+     +----------+
 *      | Sensor 2 |     | Sensor X |
 *      ------------ ... ------------
 *      |    0     |     |    0     |
 *      +----------+     +----------+      +-----------+
 *                  \      |               |  params   |
 *                   \     |               | (output)  |
 *    +----------+    \    |               +-----------+
 *    | Sensor 1 |     v   v                     |
 *    ------------      +------+------+          |
 *    |    0     |----->|  0   |  1   |<---------+
 *    +----------+      |------+------|
 *                      |     ISP     |
 *                      |------+------|
 *        +-------------|  2   |  3   |----------+
 *        |             +------+------+          |
 *        |                |                     |
 *        v                v                     v
 *  +- ---------+    +-----------+         +-----------+
 *  |     0     |    |     0     |         |   stats   |
 *  -------------    -------------         | (capture) |
 *  |  Resizer  |    |  Resizer  |         +-----------+
 *  ------------|    ------------|
 *  |     1     |    |     1     |
 *  +-----------+    +-----------+
 *        |                |
 *        v                v
 *  +-----------+    +-----------+
 *  | selfpath  |    | mainpath  |
 *  | (capture) |    | (capture) |
 *  +-----------+    +-----------+
 */

struct rkisp1_match_data {
	const char * const *clks;
	unsigned int size;
	enum rkisp1_cif_isp_version isp_ver;
};

/* ----------------------------------------------------------------------------
 * Sensor DT bindings
 */

static int rkisp1_create_links(struct rkisp1_device *rkisp1)
{
	struct media_entity *source, *sink;
	unsigned int flags, source_pad;
	struct v4l2_subdev *sd;
	unsigned int i;
	int ret;

	/* sensor links */
	flags = MEDIA_LNK_FL_ENABLED;
	list_for_each_entry(sd, &rkisp1->v4l2_dev.subdevs, list) {
		if (sd == &rkisp1->isp.sd ||
		    sd == &rkisp1->resizer_devs[RKISP1_MAINPATH].sd ||
		    sd == &rkisp1->resizer_devs[RKISP1_SELFPATH].sd)
			continue;

		ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode,
						  MEDIA_PAD_FL_SOURCE);
		if (ret < 0) {
			dev_err(rkisp1->dev, "failed to find src pad for %s\n",
				sd->name);
			return ret;
		}
		source_pad = ret;

		ret = media_create_pad_link(&sd->entity, source_pad,
					    &rkisp1->isp.sd.entity,
					    RKISP1_ISP_PAD_SINK_VIDEO,
					    flags);
		if (ret)
			return ret;

		flags = 0;
	}

	flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;

	/* create ISP->RSZ->CAP links */
	for (i = 0; i < 2; i++) {
		source = &rkisp1->isp.sd.entity;
		sink = &rkisp1->resizer_devs[i].sd.entity;
		ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_VIDEO,
					    sink, RKISP1_RSZ_PAD_SINK,
					    MEDIA_LNK_FL_ENABLED);
		if (ret)
			return ret;

		source = sink;
		sink = &rkisp1->capture_devs[i].vnode.vdev.entity;
		ret = media_create_pad_link(source, RKISP1_RSZ_PAD_SRC,
					    sink, 0, flags);
		if (ret)
			return ret;
	}

	/* params links */
	source = &rkisp1->params.vnode.vdev.entity;
	sink = &rkisp1->isp.sd.entity;
	ret = media_create_pad_link(source, 0, sink,
				    RKISP1_ISP_PAD_SINK_PARAMS, flags);
	if (ret)
		return ret;

	/* 3A stats links */
	source = &rkisp1->isp.sd.entity;
	sink = &rkisp1->stats.vnode.vdev.entity;
	return media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_STATS,
				     sink, 0, flags);
}

static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
					struct v4l2_subdev *sd,
					struct v4l2_async_subdev *asd)
{
	struct rkisp1_device *rkisp1 =
		container_of(notifier, struct rkisp1_device, notifier);
	struct rkisp1_sensor_async *s_asd =
		container_of(asd, struct rkisp1_sensor_async, asd);

	s_asd->pixel_rate_ctrl = v4l2_ctrl_find(sd->ctrl_handler,
						V4L2_CID_PIXEL_RATE);
	s_asd->sd = sd;
	s_asd->dphy = devm_phy_get(rkisp1->dev, "dphy");
	if (IS_ERR(s_asd->dphy)) {
		if (PTR_ERR(s_asd->dphy) != -EPROBE_DEFER)
			dev_err(rkisp1->dev, "Couldn't get the MIPI D-PHY\n");
		return PTR_ERR(s_asd->dphy);
	}

	phy_init(s_asd->dphy);

	return 0;
}

static void rkisp1_subdev_notifier_unbind(struct v4l2_async_notifier *notifier,
					  struct v4l2_subdev *sd,
					  struct v4l2_async_subdev *asd)
{
	struct rkisp1_sensor_async *s_asd =
		container_of(asd, struct rkisp1_sensor_async, asd);

	phy_exit(s_asd->dphy);
}

static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
{
	struct rkisp1_device *rkisp1 =
		container_of(notifier, struct rkisp1_device, notifier);
	int ret;

	ret = rkisp1_create_links(rkisp1);
	if (ret)
		return ret;

	ret = v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev);
	if (ret)
		return ret;

	dev_dbg(rkisp1->dev, "Async subdev notifier completed\n");

	return 0;
}

static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
	.bound = rkisp1_subdev_notifier_bound,
	.unbind = rkisp1_subdev_notifier_unbind,
	.complete = rkisp1_subdev_notifier_complete,
};

static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1)
{
	struct v4l2_async_notifier *ntf = &rkisp1->notifier;
	unsigned int next_id = 0;
	int ret;

	v4l2_async_notifier_init(ntf);

	while (1) {
		struct v4l2_fwnode_endpoint vep = {
			.bus_type = V4L2_MBUS_CSI2_DPHY
		};
		struct rkisp1_sensor_async *rk_asd;
		struct fwnode_handle *ep;

		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev),
						     0, next_id,
						     FWNODE_GRAPH_ENDPOINT_NEXT);
		if (!ep)
			break;

		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
		if (ret)
			goto err_parse;

		rk_asd = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep,
							struct rkisp1_sensor_async);
		if (IS_ERR(rk_asd)) {
			ret = PTR_ERR(rk_asd);
			goto err_parse;
		}

		rk_asd->mbus_type = vep.bus_type;
		rk_asd->mbus_flags = vep.bus.mipi_csi2.flags;
		rk_asd->lanes = vep.bus.mipi_csi2.num_data_lanes;

		dev_dbg(rkisp1->dev, "registered ep id %d with %d lanes\n",
			vep.base.id, rk_asd->lanes);

		next_id = vep.base.id + 1;

		fwnode_handle_put(ep);

		continue;
err_parse:
		fwnode_handle_put(ep);
		v4l2_async_notifier_cleanup(ntf);
		return ret;
	}

	if (next_id == 0)
		dev_dbg(rkisp1->dev, "no remote subdevice found\n");
	ntf->ops = &rkisp1_subdev_notifier_ops;
	ret = v4l2_async_notifier_register(&rkisp1->v4l2_dev, ntf);
	if (ret) {
		v4l2_async_notifier_cleanup(ntf);
		return ret;
	}
	return 0;
}

/* ----------------------------------------------------------------------------
 * Power
 */

static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
{
	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);

	clk_bulk_disable_unprepare(rkisp1->clk_size, rkisp1->clks);
	return pinctrl_pm_select_sleep_state(dev);
}

static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
{
	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
	int ret;

	ret = pinctrl_pm_select_default_state(dev);
	if (ret)
		return ret;
	ret = clk_bulk_prepare_enable(rkisp1->clk_size, rkisp1->clks);
	if (ret)
		return ret;

	return 0;
}

static const struct dev_pm_ops rkisp1_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
				pm_runtime_force_resume)
	SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL)
};

/* ----------------------------------------------------------------------------
 * Core
 */

static int rkisp1_entities_register(struct rkisp1_device *rkisp1)
{
	int ret;

	ret = rkisp1_isp_register(rkisp1);
	if (ret)
		return ret;

	ret = rkisp1_resizer_devs_register(rkisp1);
	if (ret)
		goto err_unreg_isp_subdev;

	ret = rkisp1_capture_devs_register(rkisp1);
	if (ret)
		goto err_unreg_resizer_devs;

	ret = rkisp1_stats_register(rkisp1);
	if (ret)
		goto err_unreg_capture_devs;

	ret = rkisp1_params_register(rkisp1);
	if (ret)
		goto err_unreg_stats;

	ret = rkisp1_subdev_notifier(rkisp1);
	if (ret) {
		dev_err(rkisp1->dev,
			"Failed to register subdev notifier(%d)\n", ret);
		goto err_unreg_params;
	}

	return 0;
err_unreg_params:
	rkisp1_params_unregister(rkisp1);
err_unreg_stats:
	rkisp1_stats_unregister(rkisp1);
err_unreg_capture_devs:
	rkisp1_capture_devs_unregister(rkisp1);
err_unreg_resizer_devs:
	rkisp1_resizer_devs_unregister(rkisp1);
err_unreg_isp_subdev:
	rkisp1_isp_unregister(rkisp1);
	return ret;
}

static irqreturn_t rkisp1_isr(int irq, void *ctx)
{
	struct device *dev = ctx;
	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);

	/*
	 * Call rkisp1_capture_isr() first to handle the frame that
	 * potentially completed using the current frame_sequence number before
	 * it is potentially incremented by rkisp1_isp_isr() in the vertical
	 * sync.
	 */
	rkisp1_capture_isr(rkisp1);
	rkisp1_isp_isr(rkisp1);
	rkisp1_mipi_isr(rkisp1);

	return IRQ_HANDLED;
}

static const char * const rk3399_isp_clks[] = {
	"isp",
	"aclk",
	"hclk",
};

static const struct rkisp1_match_data rk3399_isp_match_data = {
	.clks = rk3399_isp_clks,
	.size = ARRAY_SIZE(rk3399_isp_clks),
	.isp_ver = RKISP1_V10,
};

static const struct of_device_id rkisp1_of_match[] = {
	{
		.compatible = "rockchip,rk3399-cif-isp",
		.data = &rk3399_isp_match_data,
	},
	{},
};
MODULE_DEVICE_TABLE(of, rkisp1_of_match);

static void rkisp1_debug_init(struct rkisp1_device *rkisp1)
{
	struct rkisp1_debug *debug = &rkisp1->debug;

	debug->debugfs_dir = debugfs_create_dir(RKISP1_DRIVER_NAME, NULL);
	debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir,
			     &debug->data_loss);
	debugfs_create_ulong("outform_size_err", 0444,  debug->debugfs_dir,
			     &debug->outform_size_error);
	debugfs_create_ulong("img_stabilization_size_error", 0444,
			     debug->debugfs_dir,
			     &debug->img_stabilization_size_error);
	debugfs_create_ulong("inform_size_error", 0444,  debug->debugfs_dir,
			     &debug->inform_size_error);
	debugfs_create_ulong("irq_delay", 0444,  debug->debugfs_dir,
			     &debug->irq_delay);
	debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir,
			     &debug->mipi_error);
	debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir,
			     &debug->stats_error);
	debugfs_create_ulong("mp_stop_timeout", 0444, debug->debugfs_dir,
			     &debug->stop_timeout[RKISP1_MAINPATH]);
	debugfs_create_ulong("sp_stop_timeout", 0444, debug->debugfs_dir,
			     &debug->stop_timeout[RKISP1_SELFPATH]);
	debugfs_create_ulong("mp_frame_drop", 0444, debug->debugfs_dir,
			     &debug->frame_drop[RKISP1_MAINPATH]);
	debugfs_create_ulong("sp_frame_drop", 0444, debug->debugfs_dir,
			     &debug->frame_drop[RKISP1_SELFPATH]);
}

static int rkisp1_probe(struct platform_device *pdev)
{
	const struct rkisp1_match_data *match_data;
	struct device *dev = &pdev->dev;
	struct rkisp1_device *rkisp1;
	struct v4l2_device *v4l2_dev;
	unsigned int i;
	int ret, irq;

	match_data = of_device_get_match_data(&pdev->dev);
	if (!match_data)
		return -ENODEV;

	rkisp1 = devm_kzalloc(dev, sizeof(*rkisp1), GFP_KERNEL);
	if (!rkisp1)
		return -ENOMEM;

	dev_set_drvdata(dev, rkisp1);
	rkisp1->dev = dev;

	mutex_init(&rkisp1->stream_lock);

	rkisp1->base_addr = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(rkisp1->base_addr))
		return PTR_ERR(rkisp1->base_addr);

	irq = platform_get_irq(pdev, 0);
	if (irq < 0)
		return irq;

	ret = devm_request_irq(dev, irq, rkisp1_isr, IRQF_SHARED,
			       dev_driver_string(dev), dev);
	if (ret) {
		dev_err(dev, "request irq failed: %d\n", ret);
		return ret;
	}

	rkisp1->irq = irq;

	for (i = 0; i < match_data->size; i++)
		rkisp1->clks[i].id = match_data->clks[i];
	ret = devm_clk_bulk_get(dev, match_data->size, rkisp1->clks);
	if (ret)
		return ret;
	rkisp1->clk_size = match_data->size;

	pm_runtime_enable(&pdev->dev);

	rkisp1->media_dev.hw_revision = match_data->isp_ver;
	strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME,
		sizeof(rkisp1->media_dev.model));
	rkisp1->media_dev.dev = &pdev->dev;
	strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO,
		sizeof(rkisp1->media_dev.bus_info));
	media_device_init(&rkisp1->media_dev);

	v4l2_dev = &rkisp1->v4l2_dev;
	v4l2_dev->mdev = &rkisp1->media_dev;
	strscpy(v4l2_dev->name, RKISP1_DRIVER_NAME, sizeof(v4l2_dev->name));

	ret = v4l2_device_register(rkisp1->dev, &rkisp1->v4l2_dev);
	if (ret)
		return ret;

	ret = media_device_register(&rkisp1->media_dev);
	if (ret) {
		dev_err(dev, "Failed to register media device: %d\n", ret);
		goto err_unreg_v4l2_dev;
	}

	ret = rkisp1_entities_register(rkisp1);
	if (ret)
		goto err_unreg_media_dev;

	rkisp1_debug_init(rkisp1);

	return 0;

err_unreg_media_dev:
	media_device_unregister(&rkisp1->media_dev);
err_unreg_v4l2_dev:
	v4l2_device_unregister(&rkisp1->v4l2_dev);
	pm_runtime_disable(&pdev->dev);
	return ret;
}

static int rkisp1_remove(struct platform_device *pdev)
{
	struct rkisp1_device *rkisp1 = platform_get_drvdata(pdev);

	v4l2_async_notifier_unregister(&rkisp1->notifier);
	v4l2_async_notifier_cleanup(&rkisp1->notifier);

	rkisp1_params_unregister(rkisp1);
	rkisp1_stats_unregister(rkisp1);
	rkisp1_capture_devs_unregister(rkisp1);
	rkisp1_resizer_devs_unregister(rkisp1);
	rkisp1_isp_unregister(rkisp1);

	media_device_unregister(&rkisp1->media_dev);
	v4l2_device_unregister(&rkisp1->v4l2_dev);

	pm_runtime_disable(&pdev->dev);

	debugfs_remove_recursive(rkisp1->debug.debugfs_dir);
	return 0;
}

static struct platform_driver rkisp1_drv = {
	.driver = {
		.name = RKISP1_DRIVER_NAME,
		.of_match_table = of_match_ptr(rkisp1_of_match),
		.pm = &rkisp1_pm_ops,
	},
	.probe = rkisp1_probe,
	.remove = rkisp1_remove,
};

module_platform_driver(rkisp1_drv);
MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
MODULE_LICENSE("Dual MIT/GPL");