media: camss: Refactor VFE power domain toggling
authorRobert Foss <robert.foss@linaro.org>
Tue, 16 Mar 2021 17:19:21 +0000 (18:19 +0100)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Mon, 22 Mar 2021 11:30:14 +0000 (12:30 +0100)
For Titan ISPs clocks fail to re-enable during vfe_get()
after any vfe has been halted and its corresponding power
domain power has been detached.

Since all of the clocks depend on all of the PDs, per
VFE PD detaching is no option for Gen2 HW.

In order to not have regressions on for Gen1 HW, refactor
the power domain management into hardware version specific
code paths.

Signed-off-by: Robert Foss <robert.foss@linaro.org>
Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/platform/qcom/camss/camss-vfe-170.c
drivers/media/platform/qcom/camss/camss-vfe-4-1.c
drivers/media/platform/qcom/camss/camss-vfe-4-7.c
drivers/media/platform/qcom/camss/camss-vfe-4-8.c
drivers/media/platform/qcom/camss/camss-vfe.c
drivers/media/platform/qcom/camss/camss-vfe.h
drivers/media/platform/qcom/camss/camss.c
drivers/media/platform/qcom/camss/camss.h

index 18d63c2c9d2b9b0d7053602ac4284ddd0c91b97b..8594d275b41d19374cbed0e452a4bb95d25e5fee 100644 (file)
@@ -695,6 +695,24 @@ out_unlock:
        spin_unlock_irqrestore(&vfe->output_lock, flags);
 }
 
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+       /* nop */
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+       return 0;
+}
+
 /*
  * vfe_queue_buffer - Add empty buffer
  * @vid: Video device structure
@@ -756,6 +774,8 @@ const struct vfe_hw_ops vfe_ops_170 = {
        .hw_version_read = vfe_hw_version_read,
        .isr_read = vfe_isr_read,
        .isr = vfe_isr,
+       .pm_domain_off = vfe_pm_domain_off,
+       .pm_domain_on = vfe_pm_domain_on,
        .reg_update_clear = vfe_reg_update_clear,
        .reg_update = vfe_reg_update,
        .subdev_init = vfe_subdev_init,
index 636c0515506e322171eee748cb5197c1e7277bde..53c56a8d45458dedbaef87b6118c48c71f28387f 100644 (file)
@@ -938,6 +938,24 @@ static irqreturn_t vfe_isr(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+       /* nop */
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+       return 0;
+}
+
 static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = {
        .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
        .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
@@ -989,6 +1007,8 @@ const struct vfe_hw_ops vfe_ops_4_1 = {
        .hw_version_read = vfe_hw_version_read,
        .isr_read = vfe_isr_read,
        .isr = vfe_isr,
+       .pm_domain_off = vfe_pm_domain_off,
+       .pm_domain_on = vfe_pm_domain_on,
        .reg_update_clear = vfe_reg_update_clear,
        .reg_update = vfe_reg_update,
        .subdev_init = vfe_subdev_init,
index f5ba09a93016a5623cd1c89c0d800abc16c26bcc..a5963521775836d61388b24e30df33b28b6fe7f8 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (C) 2015-2018 Linaro Ltd.
  */
 
+#include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
@@ -1104,6 +1105,42 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
        writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
 }
 
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+       struct camss *camss;
+
+       if (!vfe)
+               return;
+
+       camss = vfe->camss;
+
+       device_link_del(camss->genpd_link[vfe->id]);
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+       struct camss *camss = vfe->camss;
+       enum vfe_line_id id = vfe->id;
+
+       camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS |
+                                               DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+       if (!camss->genpd_link[id]) {
+               dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static void vfe_violation_read(struct vfe_device *vfe)
 {
        u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
@@ -1162,6 +1199,8 @@ const struct vfe_hw_ops vfe_ops_4_7 = {
        .hw_version_read = vfe_hw_version_read,
        .isr_read = vfe_isr_read,
        .isr = vfe_isr,
+       .pm_domain_off = vfe_pm_domain_off,
+       .pm_domain_on = vfe_pm_domain_on,
        .reg_update_clear = vfe_reg_update_clear,
        .reg_update = vfe_reg_update,
        .subdev_init = vfe_subdev_init,
index 8c3a5d293a1c7ac83f6f65b4475ee961060c198c..998429dbb65cd2256877ce37d41fa80e1bf6edd0 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (C) 2015-2021 Linaro Ltd.
  */
 
+#include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
@@ -1093,6 +1094,37 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
        writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
 }
 
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+       struct camss *camss = vfe->camss;
+
+       device_link_del(camss->genpd_link[vfe->id]);
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+       struct camss *camss = vfe->camss;
+       enum vfe_line_id id = vfe->id;
+
+       camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS |
+                                               DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+       if (!camss->genpd_link[id]) {
+               dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static void vfe_violation_read(struct vfe_device *vfe)
 {
        u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
@@ -1151,6 +1183,8 @@ const struct vfe_hw_ops vfe_ops_4_8 = {
        .hw_version_read = vfe_hw_version_read,
        .isr_read = vfe_isr_read,
        .isr = vfe_isr,
+       .pm_domain_off = vfe_pm_domain_off,
+       .pm_domain_on = vfe_pm_domain_on,
        .reg_update_clear = vfe_reg_update_clear,
        .reg_update = vfe_reg_update,
        .subdev_init = vfe_subdev_init,
index c4b2c8edb760f8236a2796feb0930d2ddd448cbd..15695fd466c4da8cc8ea685d7b1b79169f8a264f 100644 (file)
@@ -580,7 +580,7 @@ static int vfe_get(struct vfe_device *vfe)
        mutex_lock(&vfe->power_lock);
 
        if (vfe->power_count == 0) {
-               ret = camss_pm_domain_on(vfe->camss, vfe->id);
+               ret = vfe->ops->pm_domain_on(vfe);
                if (ret < 0)
                        goto error_pm_domain;
 
@@ -620,7 +620,7 @@ error_reset:
 
 error_pm_runtime_get:
        pm_runtime_put_sync(vfe->camss->dev);
-       camss_pm_domain_off(vfe->camss, vfe->id);
+       vfe->ops->pm_domain_off(vfe);
 
 error_pm_domain:
        mutex_unlock(&vfe->power_lock);
@@ -646,7 +646,7 @@ static void vfe_put(struct vfe_device *vfe)
                }
                camss_disable_clocks(vfe->nclocks, vfe->clock);
                pm_runtime_put_sync(vfe->camss->dev);
-               camss_pm_domain_off(vfe->camss, vfe->id);
+               vfe->ops->pm_domain_off(vfe);
        }
 
        vfe->power_count--;
index 0a594b12a4f1e2595d218018ca792c2020c826f7..844b9275031d90b61a2d35c84007eb4e5d3bf58f 100644 (file)
@@ -106,6 +106,8 @@ struct vfe_hw_ops {
        void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
        irqreturn_t (*isr)(int irq, void *dev);
        void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1);
+       void (*pm_domain_off)(struct vfe_device *vfe);
+       int (*pm_domain_on)(struct vfe_device *vfe);
        void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id);
        void (*reg_update_clear)(struct vfe_device *vfe,
                                 enum vfe_line_id line_id);
index d022aed47e251548bd8aaf37920c1932de4edef7..9b23285d1c208a1e00cfd03368a349fe0ae24b64 100644 (file)
@@ -799,24 +799,24 @@ int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock)
 
 int camss_pm_domain_on(struct camss *camss, int id)
 {
-       if (camss->version == CAMSS_8x96 ||
-           camss->version == CAMSS_660) {
-               camss->genpd_link[id] = device_link_add(camss->dev,
-                               camss->genpd[id], DL_FLAG_STATELESS |
-                               DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+       int ret = 0;
+
+       if (id < camss->vfe_num) {
+               struct vfe_device *vfe = &camss->vfe[id];
 
-               if (!camss->genpd_link[id])
-                       return -EINVAL;
+               ret = vfe->ops->pm_domain_on(vfe);
        }
 
-       return 0;
+       return ret;
 }
 
 void camss_pm_domain_off(struct camss *camss, int id)
 {
-       if (camss->version == CAMSS_8x96 ||
-           camss->version == CAMSS_660)
-               device_link_del(camss->genpd_link[id]);
+       if (id < camss->vfe_num) {
+               struct vfe_device *vfe = &camss->vfe[id];
+
+               vfe->ops->pm_domain_off(vfe);
+       }
 }
 
 /*
@@ -1234,6 +1234,47 @@ static const struct media_device_ops camss_media_ops = {
        .link_notify = v4l2_pipeline_link_notify,
 };
 
+static int camss_configure_pd(struct camss *camss)
+{
+       int nbr_pm_domains = 0;
+       int last_pm_domain = 0;
+       int i;
+       int ret;
+
+       if (camss->version == CAMSS_8x96 ||
+           camss->version == CAMSS_660)
+               nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
+
+       for (i = 0; i < nbr_pm_domains; i++) {
+               camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i);
+               if (IS_ERR(camss->genpd[i])) {
+                       ret = PTR_ERR(camss->genpd[i]);
+                       goto fail_pm;
+               }
+
+               camss->genpd_link[i] = device_link_add(camss->dev, camss->genpd[i],
+                                                      DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+                                                      DL_FLAG_RPM_ACTIVE);
+               if (!camss->genpd_link[i]) {
+                       dev_pm_domain_detach(camss->genpd[i], true);
+                       ret = -EINVAL;
+                       goto fail_pm;
+               }
+
+               last_pm_domain = i;
+       }
+
+       return 0;
+
+fail_pm:
+       for (i = 0; i < last_pm_domain; i++) {
+               device_link_del(camss->genpd_link[i]);
+               dev_pm_domain_detach(camss->genpd[i], true);
+       }
+
+       return ret;
+}
+
 /*
  * camss_probe - Probe CAMSS platform device
  * @pdev: Pointer to CAMSS platform device
@@ -1366,20 +1407,10 @@ static int camss_probe(struct platform_device *pdev)
                }
        }
 
-       if (camss->version == CAMSS_8x96 ||
-           camss->version == CAMSS_660) {
-               camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id(
-                                               camss->dev, PM_DOMAIN_VFE0);
-               if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0]))
-                       return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]);
-
-               camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id(
-                                               camss->dev, PM_DOMAIN_VFE1);
-               if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) {
-                       dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0],
-                                            true);
-                       return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]);
-               }
+       ret = camss_configure_pd(camss);
+       if (ret < 0) {
+               dev_err(dev, "Failed to configure power domains: %d\n", ret);
+               return ret;
        }
 
        pm_runtime_enable(dev);
@@ -1400,6 +1431,9 @@ err_free:
 
 void camss_delete(struct camss *camss)
 {
+       int nbr_pm_domains = 0;
+       int i;
+
        v4l2_device_unregister(&camss->v4l2_dev);
        media_device_unregister(&camss->media_dev);
        media_device_cleanup(&camss->media_dev);
@@ -1407,9 +1441,12 @@ void camss_delete(struct camss *camss)
        pm_runtime_disable(camss->dev);
 
        if (camss->version == CAMSS_8x96 ||
-           camss->version == CAMSS_660) {
-               dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true);
-               dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true);
+           camss->version == CAMSS_660)
+               nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
+
+       for (i = 0; i < nbr_pm_domains; i++) {
+               device_link_del(camss->genpd_link[i]);
+               dev_pm_domain_detach(camss->genpd[i], true);
        }
 
        kfree(camss);
index 0f8f77801001fb377efff8f7e9c33d229bfc5b26..33ed16ab821d987dd9537fbeb8d9414959e76b60 100644 (file)
@@ -57,9 +57,9 @@ struct resources_ispif {
 };
 
 enum pm_domain {
-       PM_DOMAIN_VFE0,
-       PM_DOMAIN_VFE1,
-       PM_DOMAIN_COUNT
+       PM_DOMAIN_VFE0 = 0,
+       PM_DOMAIN_VFE1 = 1,
+       PM_DOMAIN_GEN1_COUNT = 2,       /* CAMSS series of ISPs */
 };
 
 enum camss_version {
@@ -83,8 +83,8 @@ struct camss {
        int vfe_num;
        struct vfe_device *vfe;
        atomic_t ref_count;
-       struct device *genpd[PM_DOMAIN_COUNT];
-       struct device_link *genpd_link[PM_DOMAIN_COUNT];
+       struct device *genpd[PM_DOMAIN_GEN1_COUNT];
+       struct device_link *genpd_link[PM_DOMAIN_GEN1_COUNT];
 };
 
 struct camss_camera_interface {