Commit 2f6f8af6 authored by Robert Foss's avatar Robert Foss Committed by Mauro Carvalho Chehab

media: camss: Refactor VFE power domain toggling

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: default avatarRobert Foss <robert.foss@linaro.org>
Reviewed-by: default avatarAndrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 2f8b6719
...@@ -695,6 +695,24 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) ...@@ -695,6 +695,24 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
spin_unlock_irqrestore(&vfe->output_lock, flags); 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 * vfe_queue_buffer - Add empty buffer
* @vid: Video device structure * @vid: Video device structure
...@@ -756,6 +774,8 @@ const struct vfe_hw_ops vfe_ops_170 = { ...@@ -756,6 +774,8 @@ const struct vfe_hw_ops vfe_ops_170 = {
.hw_version_read = vfe_hw_version_read, .hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read, .isr_read = vfe_isr_read,
.isr = vfe_isr, .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_clear = vfe_reg_update_clear,
.reg_update = vfe_reg_update, .reg_update = vfe_reg_update,
.subdev_init = vfe_subdev_init, .subdev_init = vfe_subdev_init,
......
...@@ -938,6 +938,24 @@ static irqreturn_t vfe_isr(int irq, void *dev) ...@@ -938,6 +938,24 @@ static irqreturn_t vfe_isr(int irq, void *dev)
return IRQ_HANDLED; 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 = { static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = {
.bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
.bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_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 = { ...@@ -989,6 +1007,8 @@ const struct vfe_hw_ops vfe_ops_4_1 = {
.hw_version_read = vfe_hw_version_read, .hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read, .isr_read = vfe_isr_read,
.isr = vfe_isr, .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_clear = vfe_reg_update_clear,
.reg_update = vfe_reg_update, .reg_update = vfe_reg_update,
.subdev_init = vfe_subdev_init, .subdev_init = vfe_subdev_init,
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Copyright (C) 2015-2018 Linaro Ltd. * Copyright (C) 2015-2018 Linaro Ltd.
*/ */
#include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
...@@ -1104,6 +1105,42 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) ...@@ -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); 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) static void vfe_violation_read(struct vfe_device *vfe)
{ {
u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
...@@ -1162,6 +1199,8 @@ const struct vfe_hw_ops vfe_ops_4_7 = { ...@@ -1162,6 +1199,8 @@ const struct vfe_hw_ops vfe_ops_4_7 = {
.hw_version_read = vfe_hw_version_read, .hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read, .isr_read = vfe_isr_read,
.isr = vfe_isr, .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_clear = vfe_reg_update_clear,
.reg_update = vfe_reg_update, .reg_update = vfe_reg_update,
.subdev_init = vfe_subdev_init, .subdev_init = vfe_subdev_init,
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Copyright (C) 2015-2021 Linaro Ltd. * Copyright (C) 2015-2021 Linaro Ltd.
*/ */
#include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
...@@ -1093,6 +1094,37 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) ...@@ -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); 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) static void vfe_violation_read(struct vfe_device *vfe)
{ {
u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
...@@ -1151,6 +1183,8 @@ const struct vfe_hw_ops vfe_ops_4_8 = { ...@@ -1151,6 +1183,8 @@ const struct vfe_hw_ops vfe_ops_4_8 = {
.hw_version_read = vfe_hw_version_read, .hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read, .isr_read = vfe_isr_read,
.isr = vfe_isr, .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_clear = vfe_reg_update_clear,
.reg_update = vfe_reg_update, .reg_update = vfe_reg_update,
.subdev_init = vfe_subdev_init, .subdev_init = vfe_subdev_init,
......
...@@ -580,7 +580,7 @@ static int vfe_get(struct vfe_device *vfe) ...@@ -580,7 +580,7 @@ static int vfe_get(struct vfe_device *vfe)
mutex_lock(&vfe->power_lock); mutex_lock(&vfe->power_lock);
if (vfe->power_count == 0) { if (vfe->power_count == 0) {
ret = camss_pm_domain_on(vfe->camss, vfe->id); ret = vfe->ops->pm_domain_on(vfe);
if (ret < 0) if (ret < 0)
goto error_pm_domain; goto error_pm_domain;
...@@ -620,7 +620,7 @@ static int vfe_get(struct vfe_device *vfe) ...@@ -620,7 +620,7 @@ static int vfe_get(struct vfe_device *vfe)
error_pm_runtime_get: error_pm_runtime_get:
pm_runtime_put_sync(vfe->camss->dev); pm_runtime_put_sync(vfe->camss->dev);
camss_pm_domain_off(vfe->camss, vfe->id); vfe->ops->pm_domain_off(vfe);
error_pm_domain: error_pm_domain:
mutex_unlock(&vfe->power_lock); mutex_unlock(&vfe->power_lock);
...@@ -646,7 +646,7 @@ static void vfe_put(struct vfe_device *vfe) ...@@ -646,7 +646,7 @@ static void vfe_put(struct vfe_device *vfe)
} }
camss_disable_clocks(vfe->nclocks, vfe->clock); camss_disable_clocks(vfe->nclocks, vfe->clock);
pm_runtime_put_sync(vfe->camss->dev); pm_runtime_put_sync(vfe->camss->dev);
camss_pm_domain_off(vfe->camss, vfe->id); vfe->ops->pm_domain_off(vfe);
} }
vfe->power_count--; vfe->power_count--;
......
...@@ -106,6 +106,8 @@ struct vfe_hw_ops { ...@@ -106,6 +106,8 @@ struct vfe_hw_ops {
void (*hw_version_read)(struct vfe_device *vfe, struct device *dev); void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
irqreturn_t (*isr)(int irq, void *dev); irqreturn_t (*isr)(int irq, void *dev);
void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1); 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)(struct vfe_device *vfe, enum vfe_line_id line_id);
void (*reg_update_clear)(struct vfe_device *vfe, void (*reg_update_clear)(struct vfe_device *vfe,
enum vfe_line_id line_id); enum vfe_line_id line_id);
......
...@@ -799,24 +799,24 @@ int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock) ...@@ -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) int camss_pm_domain_on(struct camss *camss, int id)
{ {
if (camss->version == CAMSS_8x96 || int ret = 0;
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);
if (!camss->genpd_link[id]) if (id < camss->vfe_num) {
return -EINVAL; struct vfe_device *vfe = &camss->vfe[id];
ret = vfe->ops->pm_domain_on(vfe);
} }
return 0; return ret;
} }
void camss_pm_domain_off(struct camss *camss, int id) void camss_pm_domain_off(struct camss *camss, int id)
{ {
if (camss->version == CAMSS_8x96 || if (id < camss->vfe_num) {
camss->version == CAMSS_660) struct vfe_device *vfe = &camss->vfe[id];
device_link_del(camss->genpd_link[id]);
vfe->ops->pm_domain_off(vfe);
}
} }
/* /*
...@@ -1234,6 +1234,47 @@ static const struct media_device_ops camss_media_ops = { ...@@ -1234,6 +1234,47 @@ static const struct media_device_ops camss_media_ops = {
.link_notify = v4l2_pipeline_link_notify, .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 * camss_probe - Probe CAMSS platform device
* @pdev: Pointer to CAMSS platform device * @pdev: Pointer to CAMSS platform device
...@@ -1366,20 +1407,10 @@ static int camss_probe(struct platform_device *pdev) ...@@ -1366,20 +1407,10 @@ static int camss_probe(struct platform_device *pdev)
} }
} }
if (camss->version == CAMSS_8x96 || ret = camss_configure_pd(camss);
camss->version == CAMSS_660) { if (ret < 0) {
camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( dev_err(dev, "Failed to configure power domains: %d\n", ret);
camss->dev, PM_DOMAIN_VFE0); return ret;
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]);
}
} }
pm_runtime_enable(dev); pm_runtime_enable(dev);
...@@ -1400,6 +1431,9 @@ static int camss_probe(struct platform_device *pdev) ...@@ -1400,6 +1431,9 @@ static int camss_probe(struct platform_device *pdev)
void camss_delete(struct camss *camss) void camss_delete(struct camss *camss)
{ {
int nbr_pm_domains = 0;
int i;
v4l2_device_unregister(&camss->v4l2_dev); v4l2_device_unregister(&camss->v4l2_dev);
media_device_unregister(&camss->media_dev); media_device_unregister(&camss->media_dev);
media_device_cleanup(&camss->media_dev); media_device_cleanup(&camss->media_dev);
...@@ -1407,9 +1441,12 @@ void camss_delete(struct camss *camss) ...@@ -1407,9 +1441,12 @@ void camss_delete(struct camss *camss)
pm_runtime_disable(camss->dev); pm_runtime_disable(camss->dev);
if (camss->version == CAMSS_8x96 || if (camss->version == CAMSS_8x96 ||
camss->version == CAMSS_660) { camss->version == CAMSS_660)
dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true);
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); kfree(camss);
......
...@@ -57,9 +57,9 @@ struct resources_ispif { ...@@ -57,9 +57,9 @@ struct resources_ispif {
}; };
enum pm_domain { enum pm_domain {
PM_DOMAIN_VFE0, PM_DOMAIN_VFE0 = 0,
PM_DOMAIN_VFE1, PM_DOMAIN_VFE1 = 1,
PM_DOMAIN_COUNT PM_DOMAIN_GEN1_COUNT = 2, /* CAMSS series of ISPs */
}; };
enum camss_version { enum camss_version {
...@@ -83,8 +83,8 @@ struct camss { ...@@ -83,8 +83,8 @@ struct camss {
int vfe_num; int vfe_num;
struct vfe_device *vfe; struct vfe_device *vfe;
atomic_t ref_count; atomic_t ref_count;
struct device *genpd[PM_DOMAIN_COUNT]; struct device *genpd[PM_DOMAIN_GEN1_COUNT];
struct device_link *genpd_link[PM_DOMAIN_COUNT]; struct device_link *genpd_link[PM_DOMAIN_GEN1_COUNT];
}; };
struct camss_camera_interface { struct camss_camera_interface {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment