Message ID | 1532343772-27382-19-git-send-email-todor.tomov@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Qualcomm Camera Subsystem driver - 8x96 support | expand |
Hi Todor, On Mon, Jul 23, 2018 at 02:02:35PM +0300, Todor Tomov wrote: > 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> > --- > drivers/media/platform/qcom/camss/camss-csid.c | 4 ++ > drivers/media/platform/qcom/camss/camss-csiphy.c | 5 ++ > drivers/media/platform/qcom/camss/camss-ispif.c | 19 ++++++- > drivers/media/platform/qcom/camss/camss-vfe.c | 13 +++++ > drivers/media/platform/qcom/camss/camss.c | 63 ++++++++++++++++++++++++ > drivers/media/platform/qcom/camss/camss.h | 11 +++++ > 6 files changed, 113 insertions(+), 2 deletions(-) > > diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c > index 627ef44..ea2b0ba 100644 > --- a/drivers/media/platform/qcom/camss/camss-csid.c > +++ b/drivers/media/platform/qcom/camss/camss-csid.c > @@ -13,6 +13,7 @@ > #include <linux/kernel.h> > #include <linux/of.h> > #include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > #include <linux/regulator/consumer.h> > #include <media/media-entity.h> > #include <media/v4l2-device.h> > @@ -316,6 +317,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) > if (on) { > u32 hw_version; > > + pm_runtime_get_sync(dev); > + > ret = regulator_enable(csid->vdda); Shouldn't the regulator be enabled in the runtime_resume callback instead? > if (ret < 0) > return ret; Note that you'll need pm_runtime_put() in in error handling here. Perhaps elsewhere, too. Can powering on the device (i.e. pm_runtime_get_sync() call) fail? > @@ -348,6 +351,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) > disable_irq(csid->irq); > camss_disable_clocks(csid->nclocks, csid->clock); > ret = regulator_disable(csid->vdda); > + pm_runtime_put_sync(dev); > } > > return ret; > diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c > index 0383e94..2db78791 100644 > --- a/drivers/media/platform/qcom/camss/camss-csiphy.c > +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c > @@ -13,6 +13,7 @@ > #include <linux/kernel.h> > #include <linux/of.h> > #include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > #include <media/media-entity.h> > #include <media/v4l2-device.h> > #include <media/v4l2-subdev.h> > @@ -240,6 +241,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) > u8 hw_version; > int ret; > > + pm_runtime_get_sync(dev); > + > ret = csiphy_set_clock_rates(csiphy); > if (ret < 0) > return ret; Like here. > @@ -259,6 +262,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) > disable_irq(csiphy->irq); > > camss_disable_clocks(csiphy->nclocks, csiphy->clock); > + > + pm_runtime_put_sync(dev); > } > > return 0; > diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c > index ed50cc5..8b04f8a 100644 > --- a/drivers/media/platform/qcom/camss/camss-ispif.c > +++ b/drivers/media/platform/qcom/camss/camss-ispif.c > @@ -14,6 +14,7 @@ > #include <linux/kernel.h> > #include <linux/mutex.h> > #include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > #include <media/media-entity.h> > #include <media/v4l2-device.h> > #include <media/v4l2-subdev.h> > @@ -169,6 +170,14 @@ static int ispif_reset(struct ispif_device *ispif) > u32 val; > 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, > ispif->clock_for_reset, > to_device(ispif)); > @@ -201,12 +210,15 @@ static int ispif_reset(struct ispif_device *ispif) > msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); > if (!time) { > dev_err(to_device(ispif), "ISPIF reset timeout\n"); > - return -EIO; > + ret = -EIO; > } > > 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,6 +244,8 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) > goto exit; > } > > + pm_runtime_get_sync(dev); > + > ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); > if (ret < 0) > goto exit; > @@ -252,6 +266,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) > goto exit; > } else if (ispif->power_count == 1) { > camss_disable_clocks(ispif->nclocks, ispif->clock); > + pm_runtime_put_sync(dev); > } > > ispif->power_count--; > diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c > index 3f589c4..4afbef8 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe.c > @@ -15,6 +15,7 @@ > #include <linux/mutex.h> > #include <linux/of.h> > #include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > #include <linux/spinlock_types.h> > #include <linux/spinlock.h> > #include <media/media-entity.h> > @@ -1981,6 +1982,12 @@ 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); > + if (ret < 0) > + goto error_pm_domain; > + > + pm_runtime_get_sync(vfe->camss->dev); > + > ret = vfe_set_clock_rates(vfe); > if (ret < 0) > goto error_clocks; > @@ -2012,6 +2019,10 @@ static int vfe_get(struct vfe_device *vfe) > camss_disable_clocks(vfe->nclocks, vfe->clock); > > error_clocks: > + pm_runtime_put_sync(vfe->camss->dev); > + camss_pm_domain_off(vfe->camss, vfe->id); > + > +error_pm_domain: > mutex_unlock(&vfe->power_lock); > > return ret; > @@ -2034,6 +2045,8 @@ static void vfe_put(struct vfe_device *vfe) > vfe_halt(vfe); > } > 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--; > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > index 171e2c9..dcc0c30 100644 > --- a/drivers/media/platform/qcom/camss/camss.c > +++ b/drivers/media/platform/qcom/camss/camss.c > @@ -14,6 +14,8 @@ > #include <linux/platform_device.h> > #include <linux/of.h> > #include <linux/of_graph.h> > +#include <linux/pm_runtime.h> > +#include <linux/pm_domain.h> > #include <linux/slab.h> > #include <linux/videodev2.h> > > @@ -393,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) > 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 > * @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; > > err_register_subdevs: > @@ -912,6 +951,13 @@ void camss_delete(struct camss *camss) > media_device_unregister(&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); > } > > @@ -947,12 +993,29 @@ static const struct of_device_id 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 = { > .probe = camss_probe, > .remove = camss_remove, > .driver = { > .name = "qcom-camss", > .of_match_table = camss_dt_match, > + .pm = &camss_pm_ops, > }, > }; > > diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h > index dff1045..418996d 100644 > --- a/drivers/media/platform/qcom/camss/camss.h > +++ b/drivers/media/platform/qcom/camss/camss.h > @@ -10,6 +10,7 @@ > #ifndef QC_MSM_CAMSS_H > #define QC_MSM_CAMSS_H > > +#include <linux/device.h> > #include <linux/types.h> > #include <media/v4l2-async.h> > #include <media/v4l2-device.h> > @@ -56,6 +57,12 @@ struct resources_ispif { > char *interrupt; > }; > > +enum pm_domain { > + PM_DOMAIN_VFE0, > + PM_DOMAIN_VFE1, > + PM_DOMAIN_COUNT > +}; > + > enum camss_version { > CAMSS_8x16, > CAMSS_8x96, > @@ -75,6 +82,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 camss_camera_interface { > @@ -99,6 +108,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock, > struct device *dev); > void camss_disable_clocks(int nclocks, struct camss_clock *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); > > #endif /* QC_MSM_CAMSS_H */ > -- > 2.7.4 >
Hi Sakari, Thank you for review. On 24.07.2018 15:49, Sakari Ailus wrote: > Hi Todor, > > On Mon, Jul 23, 2018 at 02:02:35PM +0300, Todor Tomov wrote: >> 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> >> --- >> drivers/media/platform/qcom/camss/camss-csid.c | 4 ++ >> drivers/media/platform/qcom/camss/camss-csiphy.c | 5 ++ >> drivers/media/platform/qcom/camss/camss-ispif.c | 19 ++++++- >> drivers/media/platform/qcom/camss/camss-vfe.c | 13 +++++ >> drivers/media/platform/qcom/camss/camss.c | 63 ++++++++++++++++++++++++ >> drivers/media/platform/qcom/camss/camss.h | 11 +++++ >> 6 files changed, 113 insertions(+), 2 deletions(-) >> >> diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c >> index 627ef44..ea2b0ba 100644 >> --- a/drivers/media/platform/qcom/camss/camss-csid.c >> +++ b/drivers/media/platform/qcom/camss/camss-csid.c >> @@ -13,6 +13,7 @@ >> #include <linux/kernel.h> >> #include <linux/of.h> >> #include <linux/platform_device.h> >> +#include <linux/pm_runtime.h> >> #include <linux/regulator/consumer.h> >> #include <media/media-entity.h> >> #include <media/v4l2-device.h> >> @@ -316,6 +317,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) >> if (on) { >> u32 hw_version; >> >> + pm_runtime_get_sync(dev); >> + >> ret = regulator_enable(csid->vdda); > > Shouldn't the regulator be enabled in the runtime_resume callback instead? Ideally - yes, but it becomes more complex (different pipelines are possible and we have only one callback) so (at least for now) I have left it as it is and stated in the commit message that suspend/resume is still not supported. > >> if (ret < 0) >> return ret; > > Note that you'll need pm_runtime_put() in in error handling here. Perhaps > elsewhere, too. Yes, I'll add it here and on all other places. > > Can powering on the device (i.e. pm_runtime_get_sync() call) fail? I'd really like to say that it cannot fail :) at least the callback is empty for now and cannot fail, but the logic in pm_runtime_get_sync() is not that simple and I'm really not sure. I'll add checks in the code in case it fails. > >> @@ -348,6 +351,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) >> disable_irq(csid->irq); >> camss_disable_clocks(csid->nclocks, csid->clock); >> ret = regulator_disable(csid->vdda); >> + pm_runtime_put_sync(dev); >> } >> >> return ret; >> diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c >> index 0383e94..2db78791 100644 >> --- a/drivers/media/platform/qcom/camss/camss-csiphy.c >> +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c >> @@ -13,6 +13,7 @@ >> #include <linux/kernel.h> >> #include <linux/of.h> >> #include <linux/platform_device.h> >> +#include <linux/pm_runtime.h> >> #include <media/media-entity.h> >> #include <media/v4l2-device.h> >> #include <media/v4l2-subdev.h> >> @@ -240,6 +241,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) >> u8 hw_version; >> int ret; >> >> + pm_runtime_get_sync(dev); >> + >> ret = csiphy_set_clock_rates(csiphy); >> if (ret < 0) >> return ret; > > Like here. Yes, I'll add it here too.
Hi Todor, On Wed, Jul 25, 2018 at 01:01:31PM +0300, Todor Tomov wrote: > Hi Sakari, > > Thank you for review. > > On 24.07.2018 15:49, Sakari Ailus wrote: > > Hi Todor, > > > > On Mon, Jul 23, 2018 at 02:02:35PM +0300, Todor Tomov wrote: > >> 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> > >> --- > >> drivers/media/platform/qcom/camss/camss-csid.c | 4 ++ > >> drivers/media/platform/qcom/camss/camss-csiphy.c | 5 ++ > >> drivers/media/platform/qcom/camss/camss-ispif.c | 19 ++++++- > >> drivers/media/platform/qcom/camss/camss-vfe.c | 13 +++++ > >> drivers/media/platform/qcom/camss/camss.c | 63 ++++++++++++++++++++++++ > >> drivers/media/platform/qcom/camss/camss.h | 11 +++++ > >> 6 files changed, 113 insertions(+), 2 deletions(-) > >> > >> diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c > >> index 627ef44..ea2b0ba 100644 > >> --- a/drivers/media/platform/qcom/camss/camss-csid.c > >> +++ b/drivers/media/platform/qcom/camss/camss-csid.c > >> @@ -13,6 +13,7 @@ > >> #include <linux/kernel.h> > >> #include <linux/of.h> > >> #include <linux/platform_device.h> > >> +#include <linux/pm_runtime.h> > >> #include <linux/regulator/consumer.h> > >> #include <media/media-entity.h> > >> #include <media/v4l2-device.h> > >> @@ -316,6 +317,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) > >> if (on) { > >> u32 hw_version; > >> > >> + pm_runtime_get_sync(dev); > >> + > >> ret = regulator_enable(csid->vdda); > > > > Shouldn't the regulator be enabled in the runtime_resume callback instead? > > Ideally - yes, but it becomes more complex (different pipelines are possible > and we have only one callback) so (at least for now) I have left it as it is > and stated in the commit message that suspend/resume is still not supported. Ack.
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 627ef44..ea2b0ba 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <media/media-entity.h> #include <media/v4l2-device.h> @@ -316,6 +317,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) if (on) { u32 hw_version; + pm_runtime_get_sync(dev); + ret = regulator_enable(csid->vdda); if (ret < 0) return ret; @@ -348,6 +351,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); ret = regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); } return ret; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 0383e94..2db78791 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> @@ -240,6 +241,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) u8 hw_version; int ret; + pm_runtime_get_sync(dev); + ret = csiphy_set_clock_rates(csiphy); if (ret < 0) return ret; @@ -259,6 +262,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) disable_irq(csiphy->irq); camss_disable_clocks(csiphy->nclocks, csiphy->clock); + + pm_runtime_put_sync(dev); } return 0; diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index ed50cc5..8b04f8a 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> @@ -169,6 +170,14 @@ static int ispif_reset(struct ispif_device *ispif) u32 val; 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, ispif->clock_for_reset, to_device(ispif)); @@ -201,12 +210,15 @@ static int ispif_reset(struct ispif_device *ispif) msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); if (!time) { dev_err(to_device(ispif), "ISPIF reset timeout\n"); - return -EIO; + ret = -EIO; } 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,6 +244,8 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } + pm_runtime_get_sync(dev); + ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); if (ret < 0) goto exit; @@ -252,6 +266,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } else if (ispif->power_count == 1) { camss_disable_clocks(ispif->nclocks, ispif->clock); + pm_runtime_put_sync(dev); } ispif->power_count--; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 3f589c4..4afbef8 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -15,6 +15,7 @@ #include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/spinlock_types.h> #include <linux/spinlock.h> #include <media/media-entity.h> @@ -1981,6 +1982,12 @@ 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); + if (ret < 0) + goto error_pm_domain; + + pm_runtime_get_sync(vfe->camss->dev); + ret = vfe_set_clock_rates(vfe); if (ret < 0) goto error_clocks; @@ -2012,6 +2019,10 @@ static int vfe_get(struct vfe_device *vfe) camss_disable_clocks(vfe->nclocks, vfe->clock); error_clocks: + pm_runtime_put_sync(vfe->camss->dev); + camss_pm_domain_off(vfe->camss, vfe->id); + +error_pm_domain: mutex_unlock(&vfe->power_lock); return ret; @@ -2034,6 +2045,8 @@ static void vfe_put(struct vfe_device *vfe) vfe_halt(vfe); } 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--; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 171e2c9..dcc0c30 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -14,6 +14,8 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_graph.h> +#include <linux/pm_runtime.h> +#include <linux/pm_domain.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -393,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) 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 * @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; err_register_subdevs: @@ -912,6 +951,13 @@ void camss_delete(struct camss *camss) media_device_unregister(&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); } @@ -947,12 +993,29 @@ static const struct of_device_id 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 = { .probe = camss_probe, .remove = camss_remove, .driver = { .name = "qcom-camss", .of_match_table = camss_dt_match, + .pm = &camss_pm_ops, }, }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index dff1045..418996d 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -10,6 +10,7 @@ #ifndef QC_MSM_CAMSS_H #define QC_MSM_CAMSS_H +#include <linux/device.h> #include <linux/types.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> @@ -56,6 +57,12 @@ struct resources_ispif { char *interrupt; }; +enum pm_domain { + PM_DOMAIN_VFE0, + PM_DOMAIN_VFE1, + PM_DOMAIN_COUNT +}; + enum camss_version { CAMSS_8x16, CAMSS_8x96, @@ -75,6 +82,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 camss_camera_interface { @@ -99,6 +108,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); void camss_disable_clocks(int nclocks, struct camss_clock *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); #endif /* QC_MSM_CAMSS_H */
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> --- drivers/media/platform/qcom/camss/camss-csid.c | 4 ++ drivers/media/platform/qcom/camss/camss-csiphy.c | 5 ++ drivers/media/platform/qcom/camss/camss-ispif.c | 19 ++++++- drivers/media/platform/qcom/camss/camss-vfe.c | 13 +++++ drivers/media/platform/qcom/camss/camss.c | 63 ++++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 11 +++++ 6 files changed, 113 insertions(+), 2 deletions(-)