mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-06 00:39:48 +00:00
media: camss: Add basic runtime PM support
There is a PM domain for each of the VFE hardware modules. Add support for basic runtime PM support to be able to control the PM domains. When a PM domain needs to be powered on - a device link is created. When a PM domain needs to be powered off - its device link is removed. This allows separate and independent control of the PM domains. Suspend/Resume is still not supported. Signed-off-by: Todor Tomov <todor.tomov@linaro.org> Signed-off-by: Hans Verkuil <hansverk@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
parent
9c3e59de60
commit
02afa816db
6 changed files with 140 additions and 7 deletions
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
|
@ -316,19 +317,27 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
|
||||||
if (on) {
|
if (on) {
|
||||||
u32 hw_version;
|
u32 hw_version;
|
||||||
|
|
||||||
ret = regulator_enable(csid->vdda);
|
ret = pm_runtime_get_sync(dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = regulator_enable(csid->vdda);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = csid_set_clock_rates(csid);
|
ret = csid_set_clock_rates(csid);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
regulator_disable(csid->vdda);
|
regulator_disable(csid->vdda);
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
|
ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
regulator_disable(csid->vdda);
|
regulator_disable(csid->vdda);
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,6 +348,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
|
||||||
disable_irq(csid->irq);
|
disable_irq(csid->irq);
|
||||||
camss_disable_clocks(csid->nclocks, csid->clock);
|
camss_disable_clocks(csid->nclocks, csid->clock);
|
||||||
regulator_disable(csid->vdda);
|
regulator_disable(csid->vdda);
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,6 +358,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
|
||||||
disable_irq(csid->irq);
|
disable_irq(csid->irq);
|
||||||
camss_disable_clocks(csid->nclocks, csid->clock);
|
camss_disable_clocks(csid->nclocks, csid->clock);
|
||||||
ret = regulator_disable(csid->vdda);
|
ret = regulator_disable(csid->vdda);
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
@ -240,13 +241,21 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
|
||||||
u8 hw_version;
|
u8 hw_version;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = csiphy_set_clock_rates(csiphy);
|
ret = pm_runtime_get_sync(dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
|
ret = csiphy_set_clock_rates(csiphy);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
enable_irq(csiphy->irq);
|
enable_irq(csiphy->irq);
|
||||||
|
|
||||||
|
@ -259,6 +268,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
|
||||||
disable_irq(csiphy->irq);
|
disable_irq(csiphy->irq);
|
||||||
|
|
||||||
camss_disable_clocks(csiphy->nclocks, csiphy->clock);
|
camss_disable_clocks(csiphy->nclocks, csiphy->clock);
|
||||||
|
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
@ -169,6 +170,14 @@ static int ispif_reset(struct ispif_device *ispif)
|
||||||
u32 val;
|
u32 val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = camss_enable_clocks(ispif->nclocks_for_reset,
|
ret = camss_enable_clocks(ispif->nclocks_for_reset,
|
||||||
ispif->clock_for_reset,
|
ispif->clock_for_reset,
|
||||||
to_device(ispif));
|
to_device(ispif));
|
||||||
|
@ -201,12 +210,15 @@ static int ispif_reset(struct ispif_device *ispif)
|
||||||
msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
|
msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
|
||||||
if (!time) {
|
if (!time) {
|
||||||
dev_err(to_device(ispif), "ISPIF reset timeout\n");
|
dev_err(to_device(ispif), "ISPIF reset timeout\n");
|
||||||
return -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);
|
camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);
|
||||||
|
|
||||||
return 0;
|
camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0);
|
||||||
|
camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -232,12 +244,19 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
|
ret = pm_runtime_get_sync(dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
|
ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ispif_reset(ispif);
|
ret = ispif_reset(ispif);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
camss_disable_clocks(ispif->nclocks, ispif->clock);
|
camss_disable_clocks(ispif->nclocks, ispif->clock);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
@ -252,6 +271,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
|
||||||
goto exit;
|
goto exit;
|
||||||
} else if (ispif->power_count == 1) {
|
} else if (ispif->power_count == 1) {
|
||||||
camss_disable_clocks(ispif->nclocks, ispif->clock);
|
camss_disable_clocks(ispif->nclocks, ispif->clock);
|
||||||
|
pm_runtime_put_sync(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
ispif->power_count--;
|
ispif->power_count--;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/spinlock_types.h>
|
#include <linux/spinlock_types.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
|
@ -1981,6 +1982,14 @@ 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);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error_pm_domain;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(vfe->camss->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error_pm_runtime_get;
|
||||||
|
|
||||||
ret = vfe_set_clock_rates(vfe);
|
ret = vfe_set_clock_rates(vfe);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error_clocks;
|
goto error_clocks;
|
||||||
|
@ -2012,6 +2021,12 @@ static int vfe_get(struct vfe_device *vfe)
|
||||||
camss_disable_clocks(vfe->nclocks, vfe->clock);
|
camss_disable_clocks(vfe->nclocks, vfe->clock);
|
||||||
|
|
||||||
error_clocks:
|
error_clocks:
|
||||||
|
pm_runtime_put_sync(vfe->camss->dev);
|
||||||
|
|
||||||
|
error_pm_runtime_get:
|
||||||
|
camss_pm_domain_off(vfe->camss, vfe->id);
|
||||||
|
|
||||||
|
error_pm_domain:
|
||||||
mutex_unlock(&vfe->power_lock);
|
mutex_unlock(&vfe->power_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2034,6 +2049,8 @@ static void vfe_put(struct vfe_device *vfe)
|
||||||
vfe_halt(vfe);
|
vfe_halt(vfe);
|
||||||
}
|
}
|
||||||
camss_disable_clocks(vfe->nclocks, vfe->clock);
|
camss_disable_clocks(vfe->nclocks, vfe->clock);
|
||||||
|
pm_runtime_put_sync(vfe->camss->dev);
|
||||||
|
camss_pm_domain_off(vfe->camss, vfe->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
vfe->power_count--;
|
vfe->power_count--;
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_graph.h>
|
#include <linux/of_graph.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
|
@ -393,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int camss_pm_domain_on(struct camss *camss, int id)
|
||||||
|
{
|
||||||
|
if (camss->version == CAMSS_8x96) {
|
||||||
|
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])
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void camss_pm_domain_off(struct camss *camss, int id)
|
||||||
|
{
|
||||||
|
if (camss->version == CAMSS_8x96)
|
||||||
|
device_link_del(camss->genpd_link[id]);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* camss_of_parse_endpoint_node - Parse port endpoint node
|
* camss_of_parse_endpoint_node - Parse port endpoint node
|
||||||
* @dev: Device
|
* @dev: Device
|
||||||
|
@ -896,6 +918,23 @@ static int camss_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (camss->version == CAMSS_8x96) {
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_register_subdevs:
|
err_register_subdevs:
|
||||||
|
@ -912,6 +951,13 @@ void camss_delete(struct camss *camss)
|
||||||
media_device_unregister(&camss->media_dev);
|
media_device_unregister(&camss->media_dev);
|
||||||
media_device_cleanup(&camss->media_dev);
|
media_device_cleanup(&camss->media_dev);
|
||||||
|
|
||||||
|
pm_runtime_disable(camss->dev);
|
||||||
|
|
||||||
|
if (camss->version == CAMSS_8x96) {
|
||||||
|
dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true);
|
||||||
|
dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true);
|
||||||
|
}
|
||||||
|
|
||||||
kfree(camss);
|
kfree(camss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,12 +993,29 @@ static const struct of_device_id camss_dt_match[] = {
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, camss_dt_match);
|
MODULE_DEVICE_TABLE(of, camss_dt_match);
|
||||||
|
|
||||||
|
static int camss_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int camss_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops camss_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||||
|
pm_runtime_force_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver qcom_camss_driver = {
|
static struct platform_driver qcom_camss_driver = {
|
||||||
.probe = camss_probe,
|
.probe = camss_probe,
|
||||||
.remove = camss_remove,
|
.remove = camss_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom-camss",
|
.name = "qcom-camss",
|
||||||
.of_match_table = camss_dt_match,
|
.of_match_table = camss_dt_match,
|
||||||
|
.pm = &camss_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#ifndef QC_MSM_CAMSS_H
|
#ifndef QC_MSM_CAMSS_H
|
||||||
#define QC_MSM_CAMSS_H
|
#define QC_MSM_CAMSS_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
|
@ -56,6 +57,12 @@ struct resources_ispif {
|
||||||
char *interrupt;
|
char *interrupt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum pm_domain {
|
||||||
|
PM_DOMAIN_VFE0,
|
||||||
|
PM_DOMAIN_VFE1,
|
||||||
|
PM_DOMAIN_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
enum camss_version {
|
enum camss_version {
|
||||||
CAMSS_8x16,
|
CAMSS_8x16,
|
||||||
CAMSS_8x96,
|
CAMSS_8x96,
|
||||||
|
@ -75,6 +82,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_link *genpd_link[PM_DOMAIN_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct camss_camera_interface {
|
struct camss_camera_interface {
|
||||||
|
@ -99,6 +108,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
|
||||||
struct device *dev);
|
struct device *dev);
|
||||||
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
|
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
|
||||||
int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock);
|
int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock);
|
||||||
|
int camss_pm_domain_on(struct camss *camss, int id);
|
||||||
|
void camss_pm_domain_off(struct camss *camss, int id);
|
||||||
void camss_delete(struct camss *camss);
|
void camss_delete(struct camss *camss);
|
||||||
|
|
||||||
#endif /* QC_MSM_CAMSS_H */
|
#endif /* QC_MSM_CAMSS_H */
|
||||||
|
|
Loading…
Reference in a new issue