Commit 02afa816 authored by Todor Tomov's avatar Todor Tomov Committed by Mauro Carvalho Chehab

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: default avatarTodor Tomov <todor.tomov@linaro.org>
Signed-off-by: default avatarHans Verkuil <hansverk@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 9c3e59de
...@@ -13,6 +13,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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 = csiphy_set_clock_rates(csiphy);
if (ret < 0) {
pm_runtime_put_sync(dev);
return ret;
}
ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
if (ret < 0) if (ret < 0) {
pm_runtime_put_sync(dev);
return ret; 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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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[] = { ...@@ -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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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, ...@@ -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 */
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