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: 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>
This commit is contained in:
Robert Foss 2021-03-16 18:19:21 +01:00 committed by Mauro Carvalho Chehab
parent 2f8b67195e
commit 2f6f8af672
8 changed files with 188 additions and 36 deletions

View 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,

View 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,

View 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,

View 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,

View 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--;

View 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);

View 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 (!camss->genpd_link[id])
return -EINVAL;
if (id < camss->vfe_num) {
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)
{
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);

View 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 {