media: ipu3-cio2: Parse information from firmware without using callbacks

Instead of using the convenience function
v4l2_async_notifier_parse_fwnode_endpoints(), parse the endpoints and set
up the async sub-devices without using callbacks. While this adds a little
bit of code, it makes parsing the endpoints quite a bit more simple and
gives more control to the driver over the process. The parsing assumes
D-PHY instead of letting the V4L2 fwnode framework guess it.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
Sakari Ailus 2019-03-04 06:29:43 -05:00 committed by Mauro Carvalho Chehab
parent 706c0cffaf
commit 2c93346698

View File

@ -1475,36 +1475,52 @@ static const struct v4l2_async_notifier_operations cio2_async_ops = {
.complete = cio2_notifier_complete, .complete = cio2_notifier_complete,
}; };
static int cio2_fwnode_parse(struct device *dev, static int cio2_parse_firmware(struct cio2_device *cio2)
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd)
{
struct sensor_async_subdev *s_asd =
container_of(asd, struct sensor_async_subdev, asd);
if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
dev_err(dev, "Only CSI2 bus type is currently supported\n");
return -EINVAL;
}
s_asd->csi2.port = vep->base.port;
s_asd->csi2.lanes = vep->bus.mipi_csi2.num_data_lanes;
return 0;
}
static int cio2_notifier_init(struct cio2_device *cio2)
{ {
unsigned int i;
int ret; int ret;
v4l2_async_notifier_init(&cio2->notifier); for (i = 0; i < CIO2_NUM_PORTS; i++) {
struct v4l2_fwnode_endpoint vep = {
.bus_type = V4L2_MBUS_CSI2_DPHY
};
struct sensor_async_subdev *s_asd = NULL;
struct fwnode_handle *ep;
ret = v4l2_async_notifier_parse_fwnode_endpoints( ep = fwnode_graph_get_endpoint_by_id(
&cio2->pci_dev->dev, &cio2->notifier, dev_fwnode(&cio2->pci_dev->dev), i, 0,
sizeof(struct sensor_async_subdev), FWNODE_GRAPH_ENDPOINT_NEXT);
cio2_fwnode_parse);
if (ret < 0) if (!ep)
goto out; continue;
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret)
goto err_parse;
s_asd = kzalloc(sizeof(*s_asd), GFP_KERNEL);
if (!s_asd) {
ret = -ENOMEM;
goto err_parse;
}
s_asd->csi2.port = vep.base.port;
s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes;
ret = v4l2_async_notifier_add_fwnode_remote_subdev(
&cio2->notifier, ep, &s_asd->asd);
if (ret)
goto err_parse;
fwnode_handle_put(ep);
continue;
err_parse:
fwnode_handle_put(ep);
kfree(s_asd);
return ret;
}
/* /*
* Proceed even without sensors connected to allow the device to * Proceed even without sensors connected to allow the device to
@ -1512,25 +1528,13 @@ static int cio2_notifier_init(struct cio2_device *cio2)
*/ */
cio2->notifier.ops = &cio2_async_ops; cio2->notifier.ops = &cio2_async_ops;
ret = v4l2_async_notifier_register(&cio2->v4l2_dev, &cio2->notifier); ret = v4l2_async_notifier_register(&cio2->v4l2_dev, &cio2->notifier);
if (ret) { if (ret)
dev_err(&cio2->pci_dev->dev, dev_err(&cio2->pci_dev->dev,
"failed to register async notifier : %d\n", ret); "failed to register async notifier : %d\n", ret);
goto out;
}
out:
if (ret)
v4l2_async_notifier_cleanup(&cio2->notifier);
return ret; return ret;
} }
static void cio2_notifier_exit(struct cio2_device *cio2)
{
v4l2_async_notifier_unregister(&cio2->notifier);
v4l2_async_notifier_cleanup(&cio2->notifier);
}
/**************** Queue initialization ****************/ /**************** Queue initialization ****************/
static const struct media_entity_operations cio2_media_ops = { static const struct media_entity_operations cio2_media_ops = {
.link_validate = v4l2_subdev_link_validate, .link_validate = v4l2_subdev_link_validate,
@ -1814,16 +1818,18 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
if (r) if (r)
goto fail_v4l2_device_unregister; goto fail_v4l2_device_unregister;
v4l2_async_notifier_init(&cio2->notifier);
/* Register notifier for subdevices we care */ /* Register notifier for subdevices we care */
r = cio2_notifier_init(cio2); r = cio2_parse_firmware(cio2);
if (r) if (r)
goto fail_cio2_queue_exit; goto fail_clean_notifier;
r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq, r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq,
IRQF_SHARED, CIO2_NAME, cio2); IRQF_SHARED, CIO2_NAME, cio2);
if (r) { if (r) {
dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r); dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r);
goto fail; goto fail_clean_notifier;
} }
pm_runtime_put_noidle(&pci_dev->dev); pm_runtime_put_noidle(&pci_dev->dev);
@ -1831,9 +1837,9 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
return 0; return 0;
fail: fail_clean_notifier:
cio2_notifier_exit(cio2); v4l2_async_notifier_unregister(&cio2->notifier);
fail_cio2_queue_exit: v4l2_async_notifier_cleanup(&cio2->notifier);
cio2_queues_exit(cio2); cio2_queues_exit(cio2);
fail_v4l2_device_unregister: fail_v4l2_device_unregister:
v4l2_device_unregister(&cio2->v4l2_dev); v4l2_device_unregister(&cio2->v4l2_dev);
@ -1852,7 +1858,8 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
struct cio2_device *cio2 = pci_get_drvdata(pci_dev); struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
media_device_unregister(&cio2->media_dev); media_device_unregister(&cio2->media_dev);
cio2_notifier_exit(cio2); v4l2_async_notifier_unregister(&cio2->notifier);
v4l2_async_notifier_cleanup(&cio2->notifier);
cio2_queues_exit(cio2); cio2_queues_exit(cio2);
cio2_fbpt_exit_dummy(cio2); cio2_fbpt_exit_dummy(cio2);
v4l2_device_unregister(&cio2->v4l2_dev); v4l2_device_unregister(&cio2->v4l2_dev);