diff options
Diffstat (limited to 'drivers/media/platform/rcar-vin')
-rw-r--r-- | drivers/media/platform/rcar-vin/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-core.c | 321 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-csi2.c | 20 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-dma.c | 63 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-v4l2.c | 18 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-vin.h | 37 |
7 files changed, 314 insertions, 147 deletions
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index baf4eafff6e3..e3eb8fee2536 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 config VIDEO_RCAR_CSI2 tristate "R-Car MIPI CSI-2 Receiver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile index 5ab803d3e7c1..00d809f5d2c1 100644 --- a/drivers/media/platform/rcar-vin/Makefile +++ b/drivers/media/platform/rcar-vin/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index d3072e166a1c..ce09799976ef 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/module.h> @@ -46,6 +42,8 @@ */ #define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4) +#define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev) + /* ----------------------------------------------------------------------------- * Media Controller link notification */ @@ -169,9 +167,37 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, /* Add the new link to the existing mask and check if it works. */ csi_id = rvin_group_entity_to_csi_id(group, link->source->entity); + + if (csi_id == -ENODEV) { + struct v4l2_subdev *sd; + unsigned int i; + + /* + * Make sure the source entity subdevice is registered as + * a parallel input of one of the enabled VINs if it is not + * one of the CSI-2 subdevices. + * + * No hardware configuration required for parallel inputs, + * we can return here. + */ + sd = media_entity_to_v4l2_subdev(link->source->entity); + for (i = 0; i < RCAR_VIN_NUM; i++) { + if (group->vin[i] && group->vin[i]->parallel && + group->vin[i]->parallel->subdev == sd) { + group->vin[i]->is_csi = false; + ret = 0; + goto out; + } + } + + vin_err(vin, "Subdevice %s not registered to any VIN\n", + link->source->entity->name); + ret = -ENODEV; + goto out; + } + channel = rvin_group_csi_pad_to_channel(link->source->index); mask_new = mask & rvin_group_get_mask(vin, csi_id, channel); - vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new); if (!mask_new) { @@ -181,6 +207,11 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, /* New valid CHSEL found, set the new value. */ ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new)); + if (ret) + goto out; + + vin->is_csi = true; + out: mutex_unlock(&group->lock); @@ -359,8 +390,6 @@ out: * Async notifier */ -#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) - static int rvin_find_pad(struct v4l2_subdev *sd, int direction) { unsigned int pad; @@ -376,12 +405,12 @@ static int rvin_find_pad(struct v4l2_subdev *sd, int direction) } /* ----------------------------------------------------------------------------- - * Digital async notifier + * Parallel async notifier */ /* The vin lock should be held when calling the subdevice attach and detach */ -static int rvin_digital_subdevice_attach(struct rvin_dev *vin, - struct v4l2_subdev *subdev) +static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, + struct v4l2_subdev *subdev) { struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -392,15 +421,20 @@ static int rvin_digital_subdevice_attach(struct rvin_dev *vin, ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); if (ret < 0) return ret; - vin->digital->source_pad = ret; + vin->parallel->source_pad = ret; ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->digital->sink_pad = ret < 0 ? 0 : ret; + vin->parallel->sink_pad = ret < 0 ? 0 : ret; + + if (vin->info->use_mc) { + vin->parallel->subdev = subdev; + return 0; + } /* Find compatible subdevices mbus format */ vin->mbus_code = 0; code.index = 0; - code.pad = vin->digital->source_pad; + code.pad = vin->parallel->source_pad; while (!vin->mbus_code && !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { code.index++; @@ -450,23 +484,27 @@ static int rvin_digital_subdevice_attach(struct rvin_dev *vin, vin->vdev.ctrl_handler = &vin->ctrl_handler; - vin->digital->subdev = subdev; + vin->parallel->subdev = subdev; return 0; } -static void rvin_digital_subdevice_detach(struct rvin_dev *vin) +static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) { rvin_v4l2_unregister(vin); - v4l2_ctrl_handler_free(&vin->ctrl_handler); + vin->parallel->subdev = NULL; - vin->vdev.ctrl_handler = NULL; - vin->digital->subdev = NULL; + if (!vin->info->use_mc) { + v4l2_ctrl_handler_free(&vin->ctrl_handler); + vin->vdev.ctrl_handler = NULL; + } } -static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) +static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); + struct media_entity *source; + struct media_entity *sink; int ret; ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); @@ -475,31 +513,50 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) return ret; } - return rvin_v4l2_register(vin); + if (!video_is_registered(&vin->vdev)) { + ret = rvin_v4l2_register(vin); + if (ret < 0) + return ret; + } + + if (!vin->info->use_mc) + return 0; + + /* If we're running with media-controller, link the subdevs. */ + source = &vin->parallel->subdev->entity; + sink = &vin->vdev.entity; + + ret = media_create_pad_link(source, vin->parallel->source_pad, + sink, vin->parallel->sink_pad, 0); + if (ret) + vin_err(vin, "Error adding link from %s to %s: %d\n", + source->name, sink->name, ret); + + return ret; } -static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); + vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); mutex_lock(&vin->lock); - rvin_digital_subdevice_detach(vin); + rvin_parallel_subdevice_detach(vin); mutex_unlock(&vin->lock); } -static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; mutex_lock(&vin->lock); - ret = rvin_digital_subdevice_attach(vin, subdev); + ret = rvin_parallel_subdevice_attach(vin, subdev); mutex_unlock(&vin->lock); if (ret) return ret; @@ -507,70 +564,71 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, v4l2_set_subdev_hostdata(subdev, vin); vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", - subdev->name, vin->digital->source_pad, - vin->digital->sink_pad); + subdev->name, vin->parallel->source_pad, + vin->parallel->sink_pad); return 0; } -static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = { - .bound = rvin_digital_notify_bound, - .unbind = rvin_digital_notify_unbind, - .complete = rvin_digital_notify_complete, +static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { + .bound = rvin_parallel_notify_bound, + .unbind = rvin_parallel_notify_unbind, + .complete = rvin_parallel_notify_complete, }; -static int rvin_digital_parse_v4l2(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) +static int rvin_parallel_parse_v4l2(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) { struct rvin_dev *vin = dev_get_drvdata(dev); - struct rvin_graph_entity *rvge = - container_of(asd, struct rvin_graph_entity, asd); + struct rvin_parallel_entity *rvpe = + container_of(asd, struct rvin_parallel_entity, asd); if (vep->base.port || vep->base.id) return -ENOTCONN; - vin->mbus_cfg.type = vep->bus_type; + vin->parallel = rvpe; + vin->parallel->mbus_type = vep->bus_type; - switch (vin->mbus_cfg.type) { + switch (vin->parallel->mbus_type) { case V4L2_MBUS_PARALLEL: vin_dbg(vin, "Found PARALLEL media bus\n"); - vin->mbus_cfg.flags = vep->bus.parallel.flags; + vin->parallel->mbus_flags = vep->bus.parallel.flags; break; case V4L2_MBUS_BT656: vin_dbg(vin, "Found BT656 media bus\n"); - vin->mbus_cfg.flags = 0; + vin->parallel->mbus_flags = 0; break; default: vin_err(vin, "Unknown media bus type\n"); return -EINVAL; } - vin->digital = rvge; - return 0; } -static int rvin_digital_graph_init(struct rvin_dev *vin) +static int rvin_parallel_init(struct rvin_dev *vin) { int ret; - ret = v4l2_async_notifier_parse_fwnode_endpoints( - vin->dev, &vin->notifier, - sizeof(struct rvin_graph_entity), rvin_digital_parse_v4l2); + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( + vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity), + 0, rvin_parallel_parse_v4l2); if (ret) return ret; - if (!vin->digital) - return -ENODEV; + /* If using mc, it's fine not to have any input registered. */ + if (!vin->parallel) + return vin->info->use_mc ? 0 : -ENODEV; - vin_dbg(vin, "Found digital subdevice %pOF\n", - to_of_node(vin->digital->asd.match.fwnode)); + vin_dbg(vin, "Found parallel subdevice %pOF\n", + to_of_node(vin->parallel->asd.match.fwnode)); - vin->notifier.ops = &rvin_digital_notify_ops; + vin->notifier.ops = &rvin_parallel_notify_ops; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); + v4l2_async_notifier_cleanup(&vin->group->notifier); return ret; } @@ -583,7 +641,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); const struct rvin_group_route *route; unsigned int i; int ret; @@ -596,7 +654,8 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) /* Register all video nodes for the group. */ for (i = 0; i < RCAR_VIN_NUM; i++) { - if (vin->group->vin[i]) { + if (vin->group->vin[i] && + !video_is_registered(&vin->group->vin[i]->vdev)) { ret = rvin_v4l2_register(vin->group->vin[i]); if (ret) return ret; @@ -649,7 +708,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; for (i = 0; i < RCAR_VIN_NUM; i++) @@ -673,7 +732,7 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; mutex_lock(&vin->group->lock); @@ -707,11 +766,9 @@ static int rvin_mc_parse_of_endpoint(struct device *dev, return -EINVAL; if (!of_device_is_available(to_of_node(asd->match.fwnode))) { - vin_dbg(vin, "OF device %pOF disabled, ignoring\n", to_of_node(asd->match.fwnode)); return -ENOTCONN; - } if (vin->group->csi[vep->base.id].fwnode) { @@ -736,12 +793,6 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) mutex_lock(&vin->group->lock); - /* If there already is a notifier something has gone wrong, bail out. */ - if (WARN_ON(vin->group->notifier)) { - mutex_unlock(&vin->group->lock); - return -EINVAL; - } - /* If not all VIN's are registered don't register the notifier. */ for (i = 0; i < RCAR_VIN_NUM; i++) if (vin->group->vin[i]) @@ -753,19 +804,16 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) } /* - * Have all VIN's look for subdevices. Some subdevices will overlap - * but the parser function can handle it, so each subdevice will - * only be registered once with the notifier. + * Have all VIN's look for CSI-2 subdevices. Some subdevices will + * overlap but the parser function can handle it, so each subdevice + * will only be registered once with the group notifier. */ - - vin->group->notifier = &vin->notifier; - for (i = 0; i < RCAR_VIN_NUM; i++) { if (!vin->group->vin[i]) continue; ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( - vin->group->vin[i]->dev, vin->group->notifier, + vin->group->vin[i]->dev, &vin->group->notifier, sizeof(struct v4l2_async_subdev), 1, rvin_mc_parse_of_endpoint); if (ret) { @@ -776,11 +824,15 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) mutex_unlock(&vin->group->lock); - vin->group->notifier->ops = &rvin_group_notify_ops; + if (!vin->group->notifier.num_subdevs) + return 0; - ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + vin->group->notifier.ops = &rvin_group_notify_ops; + ret = v4l2_async_notifier_register(&vin->v4l2_dev, + &vin->group->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); + v4l2_async_notifier_cleanup(&vin->group->notifier); return ret; } @@ -791,10 +843,6 @@ static int rvin_mc_init(struct rvin_dev *vin) { int ret; - /* All our sources are CSI-2 */ - vin->mbus_cfg.type = V4L2_MBUS_CSI2; - vin->mbus_cfg.flags = 0; - vin->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); if (ret) @@ -974,7 +1022,51 @@ static const struct rvin_info rcar_info_r8a7796 = { .routes = rcar_info_r8a7796_routes, }; -static const struct rvin_group_route _rcar_info_r8a77970_routes[] = { +static const struct rvin_group_route rcar_info_r8a77965_routes[] = { + { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77965 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a77965_routes, +}; + +static const struct rvin_group_route rcar_info_r8a77970_routes[] = { { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, @@ -990,7 +1082,19 @@ static const struct rvin_info rcar_info_r8a77970 = { .use_mc = true, .max_width = 4096, .max_height = 4096, - .routes = _rcar_info_r8a77970_routes, + .routes = rcar_info_r8a77970_routes, +}; + +static const struct rvin_group_route rcar_info_r8a77995_routes[] = { + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77995 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a77995_routes, }; static const struct of_device_id rvin_of_id_table[] = { @@ -1031,9 +1135,17 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_r8a7796, }, { + .compatible = "renesas,vin-r8a77965", + .data = &rcar_info_r8a77965, + }, + { .compatible = "renesas,vin-r8a77970", .data = &rcar_info_r8a77970, }, + { + .compatible = "renesas,vin-r8a77995", + .data = &rcar_info_r8a77995, + }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); @@ -1085,20 +1197,35 @@ static int rcar_vin_probe(struct platform_device *pdev) return ret; platform_set_drvdata(pdev, vin); - if (vin->info->use_mc) + + if (vin->info->use_mc) { ret = rvin_mc_init(vin); - else - ret = rvin_digital_graph_init(vin); - if (ret < 0) - goto error; + if (ret) + goto error_dma_unregister; + } + + ret = rvin_parallel_init(vin); + if (ret) + goto error_group_unregister; pm_suspend_ignore_children(&pdev->dev, true); pm_runtime_enable(&pdev->dev); return 0; -error: + +error_group_unregister: + if (vin->info->use_mc) { + mutex_lock(&vin->group->lock); + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); + } + mutex_unlock(&vin->group->lock); + rvin_group_put(vin); + } + +error_dma_unregister: rvin_dma_unregister(vin); - v4l2_async_notifier_cleanup(&vin->notifier); return ret; } @@ -1116,8 +1243,10 @@ static int rcar_vin_remove(struct platform_device *pdev) if (vin->info->use_mc) { mutex_lock(&vin->group->lock); - if (vin->group->notifier == &vin->notifier) - vin->group->notifier = NULL; + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); + } mutex_unlock(&vin->group->lock); rvin_group_put(vin); } else { @@ -1142,4 +1271,4 @@ module_platform_driver(rcar_vin_driver); MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index daef72d410a3..dc5ae8025832 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -339,6 +339,7 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); + int (*confirm_start)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; bool clear_ulps; @@ -545,6 +546,13 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret) return ret; + /* Confirm start */ + if (priv->info->confirm_start) { + ret = priv->info->confirm_start(priv); + if (ret) + return ret; + } + /* Clear Ultra Low Power interrupt. */ if (priv->info->clear_ulps) rcsi2_write(priv, INTSTATE_REG, @@ -881,6 +889,11 @@ static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) { + return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); +} + +static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) +{ static const struct phtw_value step1[] = { { .data = 0xed, .code = 0x34 }, { .data = 0xed, .code = 0x44 }, @@ -890,12 +903,6 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) { /* sentinel */ }, }; - int ret; - - ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); - if (ret) - return ret; - return rcsi2_phtw_write_array(priv, step1); } @@ -949,6 +956,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { .init_phtw = rcsi2_init_phtw_v3m_e3, + .confirm_start = rcsi2_confirm_start_v3m_e3, }; static const struct of_device_id rcar_csi2_of_table[] = { diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index ac07f99e3516..92323310f735 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/delay.h> @@ -123,6 +119,7 @@ /* Video n Data Mode Register 2 bits */ #define VNDMR2_VPS (1 << 30) #define VNDMR2_HPS (1 << 29) +#define VNDMR2_CES (1 << 28) #define VNDMR2_FTEV (1 << 17) #define VNDMR2_VLV(n) ((n & 0xf) << 12) @@ -659,8 +656,12 @@ static int rvin_setup(struct rvin_dev *vin) break; case MEDIA_BUS_FMT_UYVY8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + if (!vin->is_csi && + vin->parallel->mbus_type == V4L2_MBUS_BT656) + vnmc |= VNMC_INF_YUV8_BT656; + else + vnmc |= VNMC_INF_YUV8_BT601; + input_is_yuv = true; break; case MEDIA_BUS_FMT_RGB888_1X24: @@ -668,8 +669,12 @@ static int rvin_setup(struct rvin_dev *vin) break; case MEDIA_BUS_FMT_UYVY10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + if (!vin->is_csi && + vin->parallel->mbus_type == V4L2_MBUS_BT656) + vnmc |= VNMC_INF_YUV10_BT656; + else + vnmc |= VNMC_INF_YUV10_BT601; + input_is_yuv = true; break; default: @@ -682,13 +687,19 @@ static int rvin_setup(struct rvin_dev *vin) else dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); - /* Hsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_HPS; + if (!vin->is_csi) { + /* Hsync Signal Polarity Select */ + if (!(vin->parallel->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; + + /* Vsync Signal Polarity Select */ + if (!(vin->parallel->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; - /* Vsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_VPS; + /* Data Enable Polarity Select */ + if (vin->parallel->mbus_flags & V4L2_MBUS_DATA_ENABLE_LOW) + dmr2 |= VNDMR2_CES; + } /* * Output format @@ -733,8 +744,8 @@ static int rvin_setup(struct rvin_dev *vin) vnmc |= VNMC_BPS; if (vin->info->model == RCAR_GEN3) { - /* Select between CSI-2 and Digital input */ - if (vin->mbus_cfg.type == V4L2_MBUS_CSI2) + /* Select between CSI-2 and parallel input */ + if (vin->is_csi) vnmc &= ~VNMC_DPINE; else vnmc |= VNMC_DPINE; @@ -856,7 +867,7 @@ static int rvin_capture_start(struct rvin_dev *vin) /* Continuous Frame Capture Mode */ rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - vin->state = RUNNING; + vin->state = STARTING; return 0; } @@ -910,6 +921,20 @@ static irqreturn_t rvin_irq(int irq, void *data) vnms = rvin_read(vin, VNMS_REG); slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; + /* + * To hand buffers back in a known order to userspace start + * to capture first from slot 0. + */ + if (vin->state == STARTING) { + if (slot != 0) { + vin_dbg(vin, "Starting sync slot: %d\n", slot); + goto done; + } + + vin_dbg(vin, "Capture start synced!\n"); + vin->state = RUNNING; + } + /* Capture frame */ if (vin->queue_buf[slot]) { vin->queue_buf[slot]->field = vin->format.field; @@ -1074,7 +1099,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) /* No media controller used, simply pass operation to subdevice. */ if (!vin->info->use_mc) { - ret = v4l2_subdev_call(vin->digital->subdev, video, s_stream, + ret = v4l2_subdev_call(vin->parallel->subdev, video, s_stream, on); return ret == -ENOIOCTLCMD ? 0 : ret; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index e78fba84d590..5a54779cfc27 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/pm_runtime.h> @@ -144,7 +140,7 @@ static int rvin_reset_format(struct rvin_dev *vin) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = vin->digital->source_pad, + .pad = vin->parallel->source_pad, }; int ret; @@ -175,7 +171,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, - .pad = vin->digital->source_pad, + .pad = vin->parallel->source_pad, }; enum v4l2_field field; u32 width, height; @@ -517,7 +513,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, if (timings->pad) return -EINVAL; - timings->pad = vin->digital->sink_pad; + timings->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -569,7 +565,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, if (cap->pad) return -EINVAL; - cap->pad = vin->digital->sink_pad; + cap->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -587,7 +583,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->digital->sink_pad; + edid->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, get_edid, edid); @@ -605,7 +601,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->digital->sink_pad; + edid->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, set_edid, edid); diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index c2aef789b491..0b13b34d03e3 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #ifndef __RCAR_VIN__ @@ -53,11 +49,13 @@ enum rvin_csi_id { /** * STOPPED - No operation in progress + * STARTING - Capture starting up * RUNNING - Operation in progress have buffers * STOPPING - Stopping operation */ enum rvin_dma_state { STOPPED = 0, + STARTING, RUNNING, STOPPING, }; @@ -73,16 +71,22 @@ struct rvin_video_format { }; /** - * struct rvin_graph_entity - Video endpoint from async framework + * struct rvin_parallel_entity - Parallel video input endpoint descriptor * @asd: sub-device descriptor for async framework * @subdev: subdevice matched using async framework + * @mbus_type: media bus type + * @mbus_flags: media bus configuration flags * @source_pad: source pad of remote subdevice * @sink_pad: sink pad of remote subdevice + * */ -struct rvin_graph_entity { +struct rvin_parallel_entity { struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; + enum v4l2_mbus_type mbus_type; + unsigned int mbus_flags; + unsigned int source_pad; unsigned int sink_pad; }; @@ -146,7 +150,8 @@ struct rvin_info { * @v4l2_dev: V4L2 device * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier - * @digital: entity in the DT for local digital subdevice + * + * @parallel: parallel input subdevice descriptor * * @group: Gen3 CSI group * @id: Gen3 group id for this VIN @@ -164,7 +169,8 @@ struct rvin_info { * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state * - * @mbus_cfg: media bus configuration from DT + * @is_csi: flag to mark the VIN as using a CSI-2 subdevice + * * @mbus_code: media bus format code * @format: active V4L2 pixel format * @@ -182,7 +188,8 @@ struct rvin_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_graph_entity *digital; + + struct rvin_parallel_entity *parallel; struct rvin_group *group; unsigned int id; @@ -199,7 +206,8 @@ struct rvin_dev { unsigned int sequence; enum rvin_dma_state state; - struct v4l2_mbus_config mbus_cfg; + bool is_csi; + u32 mbus_code; struct v4l2_pix_format format; @@ -209,7 +217,7 @@ struct rvin_dev { v4l2_std_id std; }; -#define vin_to_source(vin) ((vin)->digital->subdev) +#define vin_to_source(vin) ((vin)->parallel->subdev) /* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) @@ -225,8 +233,7 @@ struct rvin_dev { * * @lock: protects the count, notifier, vin and csi members * @count: number of enabled VIN instances found in DT - * @notifier: pointer to the notifier of a VIN which handles the - * groups async sub-devices. + * @notifier: group notifier for CSI-2 async subdevices * @vin: VIN instances which are part of the group * @csi: array of pairs of fwnode and subdev pointers * to all CSI-2 subdevices. @@ -238,7 +245,7 @@ struct rvin_group { struct mutex lock; unsigned int count; - struct v4l2_async_notifier *notifier; + struct v4l2_async_notifier notifier; struct rvin_dev *vin[RCAR_VIN_NUM]; struct { |