summaryrefslogtreecommitdiff
path: root/drivers/media/platform/rcar-vin/rcar-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/rcar-vin/rcar-core.c')
-rw-r--r--drivers/media/platform/rcar-vin/rcar-core.c221
1 files changed, 145 insertions, 76 deletions
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 34d003e0e9b9..98bff765b02e 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -185,8 +185,8 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags,
*/
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) {
+ if (group->vin[i] &&
+ group->vin[i]->parallel.subdev == sd) {
group->vin[i]->is_csi = false;
ret = 0;
goto out;
@@ -440,20 +440,20 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
if (ret < 0)
return ret;
- vin->parallel->source_pad = ret;
+ vin->parallel.source_pad = ret;
ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
- vin->parallel->sink_pad = ret < 0 ? 0 : ret;
+ vin->parallel.sink_pad = ret < 0 ? 0 : ret;
if (vin->info->use_mc) {
- vin->parallel->subdev = subdev;
+ vin->parallel.subdev = subdev;
return 0;
}
/* Find compatible subdevices mbus format */
vin->mbus_code = 0;
code.index = 0;
- code.pad = vin->parallel->source_pad;
+ code.pad = vin->parallel.source_pad;
while (!vin->mbus_code &&
!v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) {
code.index++;
@@ -512,7 +512,7 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
vin->vdev.ctrl_handler = &vin->ctrl_handler;
- vin->parallel->subdev = subdev;
+ vin->parallel.subdev = subdev;
return 0;
}
@@ -520,7 +520,7 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
static void rvin_parallel_subdevice_detach(struct rvin_dev *vin)
{
rvin_v4l2_unregister(vin);
- vin->parallel->subdev = NULL;
+ vin->parallel.subdev = NULL;
if (!vin->info->use_mc) {
v4l2_ctrl_handler_free(&vin->ctrl_handler);
@@ -551,11 +551,11 @@ static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier)
return 0;
/* If we're running with media-controller, link the subdevs. */
- source = &vin->parallel->subdev->entity;
+ 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);
+ 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);
@@ -592,8 +592,8 @@ static int rvin_parallel_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->parallel->source_pad,
- vin->parallel->sink_pad);
+ subdev->name, vin->parallel.source_pad,
+ vin->parallel.sink_pad);
return 0;
}
@@ -604,33 +604,56 @@ static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = {
.complete = rvin_parallel_notify_complete,
};
-static int rvin_parallel_parse_v4l2(struct device *dev,
- struct v4l2_fwnode_endpoint *vep,
- struct v4l2_async_subdev *asd)
+static int rvin_parallel_parse_of(struct rvin_dev *vin)
{
- struct rvin_dev *vin = dev_get_drvdata(dev);
- struct rvin_parallel_entity *rvpe =
- container_of(asd, struct rvin_parallel_entity, asd);
+ struct fwnode_handle *ep, *fwnode;
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_UNKNOWN,
+ };
+ struct v4l2_async_subdev *asd;
+ int ret;
- if (vep->base.port || vep->base.id)
- return -ENOTCONN;
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0);
+ if (!ep)
+ return 0;
- vin->parallel = rvpe;
- vin->parallel->mbus_type = vep->bus_type;
+ fwnode = fwnode_graph_get_remote_endpoint(ep);
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ fwnode_handle_put(ep);
+ if (ret) {
+ vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode));
+ ret = -EINVAL;
+ goto out;
+ }
- switch (vin->parallel->mbus_type) {
+ switch (vep.bus_type) {
case V4L2_MBUS_PARALLEL:
case V4L2_MBUS_BT656:
vin_dbg(vin, "Found %s media bus\n",
- vin->parallel->mbus_type == V4L2_MBUS_PARALLEL ?
+ vep.bus_type == V4L2_MBUS_PARALLEL ?
"PARALLEL" : "BT656");
- vin->parallel->bus = vep->bus.parallel;
+ vin->parallel.mbus_type = vep.bus_type;
+ vin->parallel.bus = vep.bus.parallel;
break;
default:
vin_err(vin, "Unknown media bus type\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ asd = v4l2_async_notifier_add_fwnode_subdev(&vin->notifier, fwnode,
+ sizeof(*asd));
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto out;
}
+ vin->parallel.asd = asd;
+
+ vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode));
+out:
+ fwnode_handle_put(fwnode);
+
return 0;
}
@@ -640,18 +663,16 @@ static int rvin_parallel_init(struct rvin_dev *vin)
v4l2_async_notifier_init(&vin->notifier);
- ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
- vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity),
- 0, rvin_parallel_parse_v4l2);
+ ret = rvin_parallel_parse_of(vin);
if (ret)
return ret;
/* If using mc, it's fine not to have any input registered. */
- if (!vin->parallel)
+ if (!vin->parallel.asd)
return vin->info->use_mc ? 0 : -ENODEV;
vin_dbg(vin, "Found parallel subdevice %pOF\n",
- to_of_node(vin->parallel->asd.match.fwnode));
+ to_of_node(vin->parallel.asd->match.fwnode));
vin->notifier.ops = &rvin_parallel_notify_ops;
ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
@@ -751,7 +772,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
mutex_lock(&vin->group->lock);
for (i = 0; i < RVIN_CSI_MAX; i++) {
- if (vin->group->csi[i].fwnode != asd->match.fwnode)
+ if (vin->group->csi[i].asd != asd)
continue;
vin->group->csi[i].subdev = NULL;
vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i);
@@ -773,7 +794,7 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
mutex_lock(&vin->group->lock);
for (i = 0; i < RVIN_CSI_MAX; i++) {
- if (vin->group->csi[i].fwnode != asd->match.fwnode)
+ if (vin->group->csi[i].asd != asd)
continue;
vin->group->csi[i].subdev = subdev;
vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i);
@@ -791,37 +812,48 @@ static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
.complete = rvin_group_notify_complete,
};
-static int rvin_mc_parse_of_endpoint(struct device *dev,
- struct v4l2_fwnode_endpoint *vep,
- struct v4l2_async_subdev *asd)
+static int rvin_mc_parse_of(struct rvin_dev *vin, unsigned int id)
{
- struct rvin_dev *vin = dev_get_drvdata(dev);
- int ret = 0;
+ struct fwnode_handle *ep, *fwnode;
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_subdev *asd;
+ int ret;
- if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX)
- return -EINVAL;
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 1, id, 0);
+ if (!ep)
+ return 0;
- 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;
+ fwnode = fwnode_graph_get_remote_endpoint(ep);
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ fwnode_handle_put(ep);
+ if (ret) {
+ vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode));
+ ret = -EINVAL;
+ goto out;
}
- mutex_lock(&vin->group->lock);
-
- if (vin->group->csi[vep->base.id].fwnode) {
- vin_dbg(vin, "OF device %pOF already handled\n",
- to_of_node(asd->match.fwnode));
+ if (!of_device_is_available(to_of_node(fwnode))) {
+ vin_dbg(vin, "OF device %pOF disabled, ignoring\n",
+ to_of_node(fwnode));
ret = -ENOTCONN;
goto out;
}
- vin->group->csi[vep->base.id].fwnode = asd->match.fwnode;
+ asd = v4l2_async_notifier_add_fwnode_subdev(&vin->group->notifier,
+ fwnode, sizeof(*asd));
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto out;
+ }
+
+ vin->group->csi[vep.base.id].asd = asd;
vin_dbg(vin, "Add group OF device %pOF to slot %u\n",
- to_of_node(asd->match.fwnode), vep->base.id);
+ to_of_node(fwnode), vep.base.id);
out:
- mutex_unlock(&vin->group->lock);
+ fwnode_handle_put(fwnode);
return ret;
}
@@ -829,7 +861,7 @@ out:
static int rvin_mc_parse_of_graph(struct rvin_dev *vin)
{
unsigned int count = 0, vin_mask = 0;
- unsigned int i;
+ unsigned int i, id;
int ret;
mutex_lock(&vin->group->lock);
@@ -860,12 +892,14 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin)
if (!(vin_mask & BIT(i)))
continue;
- ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
- vin->group->vin[i]->dev, &vin->group->notifier,
- sizeof(struct v4l2_async_subdev), 1,
- rvin_mc_parse_of_endpoint);
- if (ret)
- return ret;
+ for (id = 0; id < RVIN_CSI_MAX; id++) {
+ if (vin->group->csi[id].asd)
+ continue;
+
+ ret = rvin_mc_parse_of(vin->group->vin[i], id);
+ if (ret)
+ return ret;
+ }
}
if (list_empty(&vin->group->notifier.asd_list))
@@ -919,6 +953,54 @@ static int rvin_mc_init(struct rvin_dev *vin)
}
/* -----------------------------------------------------------------------------
+ * Suspend / Resume
+ */
+
+static int __maybe_unused rvin_suspend(struct device *dev)
+{
+ struct rvin_dev *vin = dev_get_drvdata(dev);
+
+ if (vin->state != RUNNING)
+ return 0;
+
+ rvin_stop_streaming(vin);
+
+ vin->state = SUSPENDED;
+
+ return 0;
+}
+
+static int __maybe_unused rvin_resume(struct device *dev)
+{
+ struct rvin_dev *vin = dev_get_drvdata(dev);
+
+ if (vin->state != SUSPENDED)
+ return 0;
+
+ /*
+ * Restore group master CHSEL setting.
+ *
+ * This needs to be done by every VIN resuming not only the master
+ * as we don't know if and in which order the master VINs will
+ * be resumed.
+ */
+ if (vin->info->use_mc) {
+ unsigned int master_id = rvin_group_id_to_master(vin->id);
+ struct rvin_dev *master = vin->group->vin[master_id];
+ int ret;
+
+ if (WARN_ON(!master))
+ return -ENODEV;
+
+ ret = rvin_set_channel_routing(master, master->chsel);
+ if (ret)
+ return ret;
+ }
+
+ return rvin_start_streaming(vin);
+}
+
+/* -----------------------------------------------------------------------------
* Platform Device Driver
*/
@@ -1268,22 +1350,6 @@ static const struct of_device_id rvin_of_id_table[] = {
.data = &rcar_info_h1,
},
{
- .compatible = "renesas,vin-r8a7790",
- .data = &rcar_info_gen2,
- },
- {
- .compatible = "renesas,vin-r8a7791",
- .data = &rcar_info_gen2,
- },
- {
- .compatible = "renesas,vin-r8a7793",
- .data = &rcar_info_gen2,
- },
- {
- .compatible = "renesas,vin-r8a7794",
- .data = &rcar_info_gen2,
- },
- {
.compatible = "renesas,rcar-gen2-vin",
.data = &rcar_info_gen2,
},
@@ -1421,9 +1487,12 @@ static int rcar_vin_remove(struct platform_device *pdev)
return 0;
}
+static SIMPLE_DEV_PM_OPS(rvin_pm_ops, rvin_suspend, rvin_resume);
+
static struct platform_driver rcar_vin_driver = {
.driver = {
.name = "rcar-vin",
+ .pm = &rvin_pm_ops,
.of_match_table = rvin_of_id_table,
},
.probe = rcar_vin_probe,