@@ -236,9 +236,15 @@ static int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q)
return 0;
}
-static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
+static int cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
{
+ if (!q->fbpt)
+ return -ENOENT;
+
dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr);
+ q->fbpt = NULL;
+
+ return 0;
}
/**************** CSI2 hardware setup ****************/
@@ -1652,13 +1658,13 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
{
- vb2_video_unregister_device(&q->vdev);
media_entity_cleanup(&q->vdev.entity);
v4l2_device_unregister_subdev(&q->subdev);
media_entity_cleanup(&q->subdev.entity);
- cio2_fbpt_exit(q, &cio2->pci_dev->dev);
- mutex_destroy(&q->subdev_lock);
- mutex_destroy(&q->lock);
+ if (!cio2_fbpt_exit(q, &cio2->pci_dev->dev)) {
+ mutex_destroy(&q->subdev_lock);
+ mutex_destroy(&q->lock);
+ }
}
static int cio2_queues_init(struct cio2_device *cio2)
@@ -1704,6 +1710,23 @@ static int cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
return cio2_check_fwnode_graph(fwnode->secondary);
}
+static void cio2_media_release(struct media_device *mdev)
+{
+ struct cio2_device *cio2 =
+ container_of(mdev, struct cio2_device, media_dev);
+
+ v4l2_async_nf_cleanup(&cio2->notifier);
+ cio2_queues_exit(cio2);
+ cio2_fbpt_exit_dummy(cio2);
+ mutex_destroy(&cio2->lock);
+
+ kfree(cio2);
+}
+
+static struct media_device_ops cio2_mdev_ops = {
+ .release = cio2_media_release,
+};
+
/**************** PCI interface ****************/
static int cio2_pci_probe(struct pci_dev *pci_dev,
@@ -1731,7 +1754,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
return r;
}
- cio2 = devm_kzalloc(dev, sizeof(*cio2), GFP_KERNEL);
+ cio2 = kzalloc(sizeof(*cio2), GFP_KERNEL);
if (!cio2)
return -ENOMEM;
cio2->pci_dev = pci_dev;
@@ -1782,6 +1805,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
mutex_init(&cio2->lock);
cio2->media_dev.dev = dev;
+ cio2->media_dev.ops = &cio2_mdev_ops;
strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME,
sizeof(cio2->media_dev.model));
cio2->media_dev.hw_revision = 0;
@@ -1816,15 +1840,18 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
fail_clean_notifier:
v4l2_async_nf_unregister(&cio2->notifier);
- v4l2_async_nf_cleanup(&cio2->notifier);
- cio2_queues_exit(cio2);
+
fail_v4l2_device_unregister:
v4l2_device_unregister(&cio2->v4l2_dev);
+
fail_media_device_unregister:
media_device_unregister(&cio2->media_dev);
- media_device_cleanup(&cio2->media_dev);
+
fail_free_irq:
free_irq(pci_dev->irq, cio2);
+ media_device_put(&cio2->media_dev);
+ return r;
+
fail_mutex_destroy:
mutex_destroy(&cio2->lock);
cio2_fbpt_exit_dummy(cio2);
@@ -1835,19 +1862,19 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
static void cio2_pci_remove(struct pci_dev *pci_dev)
{
struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
+ unsigned int i;
media_device_unregister(&cio2->media_dev);
+ for (i = 0; i < CIO2_QUEUES; i++)
+ vb2_video_unregister_device(&cio2->queue[i].vdev);
v4l2_device_unregister(&cio2->v4l2_dev);
free_irq(pci_dev->irq, cio2);
v4l2_async_nf_unregister(&cio2->notifier);
- v4l2_async_nf_cleanup(&cio2->notifier);
- cio2_queues_exit(cio2);
- cio2_fbpt_exit_dummy(cio2);
- media_device_cleanup(&cio2->media_dev);
- mutex_destroy(&cio2->lock);
pm_runtime_forbid(&pci_dev->dev);
pm_runtime_get_noresume(&pci_dev->dev);
+
+ media_device_put(&cio2->media_dev);
}
static int __maybe_unused cio2_runtime_suspend(struct device *dev)
Use the media device release callback to release the cio2 device's data structure. This approach has the benefit of not releasing memory which may still be accessed through open file handles whilst the ipu3-cio2 driver is being unbound. Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> --- drivers/media/pci/intel/ipu3/ipu3-cio2-main.c | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-)