|
@@ -19,12 +19,15 @@
|
|
|
#include <linux/of.h>
|
|
|
#include <linux/of_device.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <linux/videodev2.h>
|
|
|
|
|
|
+#include <media/rcar-fcp.h>
|
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
|
|
#include "vsp1.h"
|
|
|
#include "vsp1_bru.h"
|
|
|
+#include "vsp1_clu.h"
|
|
|
#include "vsp1_dl.h"
|
|
|
#include "vsp1_drm.h"
|
|
|
#include "vsp1_hsit.h"
|
|
@@ -145,7 +148,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (vsp1->info->features & VSP1_HAS_LIF) {
|
|
|
+ if (vsp1->lif) {
|
|
|
ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
|
|
|
RWPF_PAD_SOURCE,
|
|
|
&vsp1->lif->entity.subdev.entity,
|
|
@@ -168,19 +171,15 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
|
|
|
|
|
|
for (i = 0; i < vsp1->info->wpf_count; ++i) {
|
|
|
/* Connect the video device to the WPF. All connections are
|
|
|
- * immutable except for the WPF0 source link if a LIF is
|
|
|
- * present.
|
|
|
+ * immutable.
|
|
|
*/
|
|
|
struct vsp1_rwpf *wpf = vsp1->wpf[i];
|
|
|
- unsigned int flags = MEDIA_LNK_FL_ENABLED;
|
|
|
-
|
|
|
- if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0)
|
|
|
- flags |= MEDIA_LNK_FL_IMMUTABLE;
|
|
|
|
|
|
ret = media_create_pad_link(&wpf->entity.subdev.entity,
|
|
|
RWPF_PAD_SOURCE,
|
|
|
&wpf->video->video.entity, 0,
|
|
|
- flags);
|
|
|
+ MEDIA_LNK_FL_IMMUTABLE |
|
|
|
+ MEDIA_LNK_FL_ENABLED);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
}
|
|
@@ -204,7 +203,8 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1)
|
|
|
}
|
|
|
|
|
|
v4l2_device_unregister(&vsp1->v4l2_dev);
|
|
|
- media_device_unregister(&vsp1->media_dev);
|
|
|
+ if (vsp1->info->uapi)
|
|
|
+ media_device_unregister(&vsp1->media_dev);
|
|
|
media_device_cleanup(&vsp1->media_dev);
|
|
|
|
|
|
if (!vsp1->info->uapi)
|
|
@@ -252,6 +252,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
|
|
|
list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
|
|
|
}
|
|
|
|
|
|
+ if (vsp1->info->features & VSP1_HAS_CLU) {
|
|
|
+ vsp1->clu = vsp1_clu_create(vsp1);
|
|
|
+ if (IS_ERR(vsp1->clu)) {
|
|
|
+ ret = PTR_ERR(vsp1->clu);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);
|
|
|
+ }
|
|
|
+
|
|
|
vsp1->hsi = vsp1_hsit_create(vsp1, true);
|
|
|
if (IS_ERR(vsp1->hsi)) {
|
|
|
ret = PTR_ERR(vsp1->hsi);
|
|
@@ -268,7 +278,11 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
|
|
|
|
|
|
list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
|
|
|
|
|
|
- if (vsp1->info->features & VSP1_HAS_LIF) {
|
|
|
+ /* The LIF is only supported when used in conjunction with the DU, in
|
|
|
+ * which case the userspace API is disabled. If the userspace API is
|
|
|
+ * enabled skip the LIF, even when present.
|
|
|
+ */
|
|
|
+ if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) {
|
|
|
vsp1->lif = vsp1_lif_create(vsp1);
|
|
|
if (IS_ERR(vsp1->lif)) {
|
|
|
ret = PTR_ERR(vsp1->lif);
|
|
@@ -379,14 +393,15 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
|
|
|
/* Register subdev nodes if the userspace API is enabled or initialize
|
|
|
* the DRM pipeline otherwise.
|
|
|
*/
|
|
|
- if (vsp1->info->uapi)
|
|
|
+ if (vsp1->info->uapi) {
|
|
|
ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
|
|
|
- else
|
|
|
- ret = vsp1_drm_init(vsp1);
|
|
|
- if (ret < 0)
|
|
|
- goto done;
|
|
|
+ if (ret < 0)
|
|
|
+ goto done;
|
|
|
|
|
|
- ret = media_device_register(mdev);
|
|
|
+ ret = media_device_register(mdev);
|
|
|
+ } else {
|
|
|
+ ret = vsp1_drm_init(vsp1);
|
|
|
+ }
|
|
|
|
|
|
done:
|
|
|
if (ret < 0)
|
|
@@ -462,35 +477,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
|
|
|
/*
|
|
|
* vsp1_device_get - Acquire the VSP1 device
|
|
|
*
|
|
|
- * Increment the VSP1 reference count and initialize the device if the first
|
|
|
- * reference is taken.
|
|
|
+ * Make sure the device is not suspended and initialize it if needed.
|
|
|
*
|
|
|
* Return 0 on success or a negative error code otherwise.
|
|
|
*/
|
|
|
int vsp1_device_get(struct vsp1_device *vsp1)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
- mutex_lock(&vsp1->lock);
|
|
|
- if (vsp1->ref_count > 0)
|
|
|
- goto done;
|
|
|
-
|
|
|
- ret = clk_prepare_enable(vsp1->clock);
|
|
|
- if (ret < 0)
|
|
|
- goto done;
|
|
|
-
|
|
|
- ret = vsp1_device_init(vsp1);
|
|
|
- if (ret < 0) {
|
|
|
- clk_disable_unprepare(vsp1->clock);
|
|
|
- goto done;
|
|
|
- }
|
|
|
-
|
|
|
-done:
|
|
|
- if (!ret)
|
|
|
- vsp1->ref_count++;
|
|
|
+ int ret;
|
|
|
|
|
|
- mutex_unlock(&vsp1->lock);
|
|
|
- return ret;
|
|
|
+ ret = pm_runtime_get_sync(vsp1->dev);
|
|
|
+ return ret < 0 ? ret : 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -501,12 +497,7 @@ done:
|
|
|
*/
|
|
|
void vsp1_device_put(struct vsp1_device *vsp1)
|
|
|
{
|
|
|
- mutex_lock(&vsp1->lock);
|
|
|
-
|
|
|
- if (--vsp1->ref_count == 0)
|
|
|
- clk_disable_unprepare(vsp1->clock);
|
|
|
-
|
|
|
- mutex_unlock(&vsp1->lock);
|
|
|
+ pm_runtime_put_sync(vsp1->dev);
|
|
|
}
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
@@ -518,14 +509,8 @@ static int vsp1_pm_suspend(struct device *dev)
|
|
|
{
|
|
|
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
|
|
|
|
|
|
- WARN_ON(mutex_is_locked(&vsp1->lock));
|
|
|
-
|
|
|
- if (vsp1->ref_count == 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
vsp1_pipelines_suspend(vsp1);
|
|
|
-
|
|
|
- clk_disable_unprepare(vsp1->clock);
|
|
|
+ pm_runtime_force_suspend(vsp1->dev);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -534,21 +519,39 @@ static int vsp1_pm_resume(struct device *dev)
|
|
|
{
|
|
|
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
|
|
|
|
|
|
- WARN_ON(mutex_is_locked(&vsp1->lock));
|
|
|
+ pm_runtime_force_resume(vsp1->dev);
|
|
|
+ vsp1_pipelines_resume(vsp1);
|
|
|
|
|
|
- if (vsp1->ref_count == 0)
|
|
|
- return 0;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
|
|
|
- clk_prepare_enable(vsp1->clock);
|
|
|
+static int vsp1_pm_runtime_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
|
|
|
|
|
|
- vsp1_pipelines_resume(vsp1);
|
|
|
+ rcar_fcp_disable(vsp1->fcp);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
-#endif
|
|
|
+
|
|
|
+static int vsp1_pm_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (vsp1->info) {
|
|
|
+ ret = vsp1_device_init(vsp1);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rcar_fcp_enable(vsp1->fcp);
|
|
|
+}
|
|
|
|
|
|
static const struct dev_pm_ops vsp1_pm_ops = {
|
|
|
SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
|
|
|
+ SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)
|
|
|
};
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
@@ -559,7 +562,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
{
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPS_H2,
|
|
|
.gen = 2,
|
|
|
- .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
|
|
|
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
|
|
|
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 5,
|
|
|
.uds_count = 3,
|
|
|
.wpf_count = 4,
|
|
@@ -568,9 +572,9 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
}, {
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPR_H2,
|
|
|
.gen = 2,
|
|
|
- .features = VSP1_HAS_BRU | VSP1_HAS_SRU,
|
|
|
+ .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 5,
|
|
|
- .uds_count = 1,
|
|
|
+ .uds_count = 3,
|
|
|
.wpf_count = 4,
|
|
|
.num_bru_inputs = 4,
|
|
|
.uapi = true,
|
|
@@ -580,22 +584,24 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
|
|
|
.rpf_count = 4,
|
|
|
.uds_count = 1,
|
|
|
- .wpf_count = 4,
|
|
|
+ .wpf_count = 1,
|
|
|
.num_bru_inputs = 4,
|
|
|
.uapi = true,
|
|
|
}, {
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPS_M2,
|
|
|
.gen = 2,
|
|
|
- .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
|
|
|
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
|
|
|
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 5,
|
|
|
- .uds_count = 3,
|
|
|
+ .uds_count = 1,
|
|
|
.wpf_count = 4,
|
|
|
.num_bru_inputs = 4,
|
|
|
.uapi = true,
|
|
|
}, {
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
|
|
|
.gen = 3,
|
|
|
- .features = VSP1_HAS_LUT | VSP1_HAS_SRU,
|
|
|
+ .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU
|
|
|
+ | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 1,
|
|
|
.uds_count = 1,
|
|
|
.wpf_count = 1,
|
|
@@ -603,7 +609,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
}, {
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
|
|
|
.gen = 3,
|
|
|
- .features = VSP1_HAS_BRU,
|
|
|
+ .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 5,
|
|
|
.wpf_count = 1,
|
|
|
.num_bru_inputs = 5,
|
|
@@ -611,7 +617,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
}, {
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
|
|
|
.gen = 3,
|
|
|
- .features = VSP1_HAS_BRU | VSP1_HAS_LUT,
|
|
|
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
|
|
|
+ | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 5,
|
|
|
.wpf_count = 1,
|
|
|
.num_bru_inputs = 5,
|
|
@@ -619,7 +626,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
}, {
|
|
|
.version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
|
|
|
.gen = 3,
|
|
|
- .features = VSP1_HAS_BRU | VSP1_HAS_LIF,
|
|
|
+ .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP,
|
|
|
.rpf_count = 5,
|
|
|
.wpf_count = 2,
|
|
|
.num_bru_inputs = 5,
|
|
@@ -629,6 +636,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
|
|
|
static int vsp1_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct vsp1_device *vsp1;
|
|
|
+ struct device_node *fcp_node;
|
|
|
struct resource *irq;
|
|
|
struct resource *io;
|
|
|
unsigned int i;
|
|
@@ -640,22 +648,17 @@ static int vsp1_probe(struct platform_device *pdev)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
vsp1->dev = &pdev->dev;
|
|
|
- mutex_init(&vsp1->lock);
|
|
|
INIT_LIST_HEAD(&vsp1->entities);
|
|
|
INIT_LIST_HEAD(&vsp1->videos);
|
|
|
|
|
|
- /* I/O, IRQ and clock resources */
|
|
|
+ platform_set_drvdata(pdev, vsp1);
|
|
|
+
|
|
|
+ /* I/O and IRQ resources (clock managed by the clock PM domain) */
|
|
|
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
|
|
|
if (IS_ERR(vsp1->mmio))
|
|
|
return PTR_ERR(vsp1->mmio);
|
|
|
|
|
|
- vsp1->clock = devm_clk_get(&pdev->dev, NULL);
|
|
|
- if (IS_ERR(vsp1->clock)) {
|
|
|
- dev_err(&pdev->dev, "failed to get clock\n");
|
|
|
- return PTR_ERR(vsp1->clock);
|
|
|
- }
|
|
|
-
|
|
|
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
|
if (!irq) {
|
|
|
dev_err(&pdev->dev, "missing IRQ\n");
|
|
@@ -669,13 +672,27 @@ static int vsp1_probe(struct platform_device *pdev)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ /* FCP (optional) */
|
|
|
+ fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
|
|
|
+ if (fcp_node) {
|
|
|
+ vsp1->fcp = rcar_fcp_get(fcp_node);
|
|
|
+ of_node_put(fcp_node);
|
|
|
+ if (IS_ERR(vsp1->fcp)) {
|
|
|
+ dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
|
|
|
+ PTR_ERR(vsp1->fcp));
|
|
|
+ return PTR_ERR(vsp1->fcp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* Configure device parameters based on the version register. */
|
|
|
- ret = clk_prepare_enable(vsp1->clock);
|
|
|
+ pm_runtime_enable(&pdev->dev);
|
|
|
+
|
|
|
+ ret = pm_runtime_get_sync(&pdev->dev);
|
|
|
if (ret < 0)
|
|
|
- return ret;
|
|
|
+ goto done;
|
|
|
|
|
|
version = vsp1_read(vsp1, VI6_IP_VERSION);
|
|
|
- clk_disable_unprepare(vsp1->clock);
|
|
|
+ pm_runtime_put_sync(&pdev->dev);
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
|
|
|
if ((version & VI6_IP_VERSION_MODEL_MASK) ==
|
|
@@ -687,7 +704,8 @@ static int vsp1_probe(struct platform_device *pdev)
|
|
|
|
|
|
if (!vsp1->info) {
|
|
|
dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version);
|
|
|
- return -ENXIO;
|
|
|
+ ret = -ENXIO;
|
|
|
+ goto done;
|
|
|
}
|
|
|
|
|
|
dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
|
|
@@ -696,12 +714,14 @@ static int vsp1_probe(struct platform_device *pdev)
|
|
|
ret = vsp1_create_entities(vsp1);
|
|
|
if (ret < 0) {
|
|
|
dev_err(&pdev->dev, "failed to create entities\n");
|
|
|
- return ret;
|
|
|
+ goto done;
|
|
|
}
|
|
|
|
|
|
- platform_set_drvdata(pdev, vsp1);
|
|
|
+done:
|
|
|
+ if (ret)
|
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int vsp1_remove(struct platform_device *pdev)
|
|
@@ -709,6 +729,9 @@ static int vsp1_remove(struct platform_device *pdev)
|
|
|
struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
|
|
|
|
|
|
vsp1_destroy_entities(vsp1);
|
|
|
+ rcar_fcp_put(vsp1->fcp);
|
|
|
+
|
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
|
|
|
|
return 0;
|
|
|
}
|