On Wed, 22 Apr 2020 at 01:00, Samuel Holland <samuel@xxxxxxxxxxxx> wrote: > > This allows the VE clocks and PLL_VE to be disabled most of the time. > A runtime PM reference is held while streaming. > > Originally-by: Jernej Škrabec <jernej.skrabec@xxxxxxxxx> Originally-by is not documented, perhaps just go with Signed-off-by, as Jernej suggested. > Signed-off-by: Samuel Holland <samuel@xxxxxxxxxxxx> > --- > > v2: moved PM reference to cedrus_{start,stop}_streaming, based on an > earlier patch by Jernej Skrabec. Removes the need for autosuspend. > I tested this with running 2x v4l2-request-test in parallel. > > --- > drivers/staging/media/sunxi/cedrus/cedrus.c | 7 ++ > .../staging/media/sunxi/cedrus/cedrus_hw.c | 106 ++++++++++++------ > .../staging/media/sunxi/cedrus/cedrus_hw.h | 3 + > .../staging/media/sunxi/cedrus/cedrus_video.c | 33 ++++-- > 4 files changed, 104 insertions(+), 45 deletions(-) > > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c > index 05a85517ff60..bc27f9430eeb 100644 > --- a/drivers/staging/media/sunxi/cedrus/cedrus.c > +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c > @@ -16,6 +16,7 @@ > #include <linux/platform_device.h> > #include <linux/module.h> > #include <linux/of.h> > +#include <linux/pm.h> > > #include <media/v4l2-device.h> > #include <media/v4l2-ioctl.h> > @@ -551,12 +552,18 @@ static const struct of_device_id cedrus_dt_match[] = { > }; > MODULE_DEVICE_TABLE(of, cedrus_dt_match); > > +static const struct dev_pm_ops cedrus_dev_pm_ops = { > + SET_RUNTIME_PM_OPS(cedrus_hw_suspend, > + cedrus_hw_resume, NULL) > +}; > + > static struct platform_driver cedrus_driver = { > .probe = cedrus_probe, > .remove = cedrus_remove, > .driver = { > .name = CEDRUS_NAME, > .of_match_table = of_match_ptr(cedrus_dt_match), > + .pm = &cedrus_dev_pm_ops, > }, > }; > module_platform_driver(cedrus_driver); > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c > index daf5f244f93b..1744e6fcc999 100644 > --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c > @@ -19,6 +19,7 @@ > #include <linux/dma-mapping.h> > #include <linux/interrupt.h> > #include <linux/clk.h> > +#include <linux/pm_runtime.h> > #include <linux/regmap.h> > #include <linux/reset.h> > #include <linux/soc/sunxi/sunxi_sram.h> > @@ -140,6 +141,64 @@ static irqreturn_t cedrus_irq(int irq, void *data) > return IRQ_HANDLED; > } > > +int cedrus_hw_suspend(struct device *device) > +{ > + struct cedrus_dev *dev = dev_get_drvdata(device); > + > + reset_control_assert(dev->rstc); > + > + clk_disable_unprepare(dev->ram_clk); > + clk_disable_unprepare(dev->mod_clk); > + clk_disable_unprepare(dev->ahb_clk); > + You can use clk_bulk API here. > + return 0; > +} > + > +int cedrus_hw_resume(struct device *device) > +{ > + struct cedrus_dev *dev = dev_get_drvdata(device); > + int ret; > + > + ret = clk_prepare_enable(dev->ahb_clk); > + if (ret) { > + dev_err(dev->dev, "Failed to enable AHB clock\n"); > + > + return ret; > + } > + > + ret = clk_prepare_enable(dev->mod_clk); > + if (ret) { > + dev_err(dev->dev, "Failed to enable MOD clock\n"); > + > + goto err_ahb_clk; > + } > + > + ret = clk_prepare_enable(dev->ram_clk); > + if (ret) { > + dev_err(dev->dev, "Failed to enable RAM clock\n"); > + > + goto err_mod_clk; > + } > + > + ret = reset_control_reset(dev->rstc); > + if (ret) { > + dev_err(dev->dev, "Failed to apply reset\n"); > + > + goto err_ram_clk; > + } > + > + return 0; > + > +err_ram_clk: > + clk_disable_unprepare(dev->ram_clk); > +err_mod_clk: > + clk_disable_unprepare(dev->mod_clk); > +err_ahb_clk: > + clk_disable_unprepare(dev->ahb_clk); > + > + return ret; > +} > + > int cedrus_hw_probe(struct cedrus_dev *dev) > { > const struct cedrus_variant *variant; > @@ -236,42 +295,17 @@ int cedrus_hw_probe(struct cedrus_dev *dev) > goto err_sram; > } > > - ret = clk_prepare_enable(dev->ahb_clk); > - if (ret) { > - dev_err(dev->dev, "Failed to enable AHB clock\n"); > - > - goto err_sram; > - } > - > - ret = clk_prepare_enable(dev->mod_clk); > - if (ret) { > - dev_err(dev->dev, "Failed to enable MOD clock\n"); > - > - goto err_ahb_clk; > - } > - > - ret = clk_prepare_enable(dev->ram_clk); > - if (ret) { > - dev_err(dev->dev, "Failed to enable RAM clock\n"); > - > - goto err_mod_clk; > - } > - > - ret = reset_control_reset(dev->rstc); > - if (ret) { > - dev_err(dev->dev, "Failed to apply reset\n"); > - > - goto err_ram_clk; > + pm_runtime_enable(dev->dev); > + if (!pm_runtime_enabled(dev->dev)) { > + ret = cedrus_hw_resume(dev->dev); > + if (ret) > + goto err_pm; > } > > return 0; > > -err_ram_clk: > - clk_disable_unprepare(dev->ram_clk); > -err_mod_clk: > - clk_disable_unprepare(dev->mod_clk); > -err_ahb_clk: > - clk_disable_unprepare(dev->ahb_clk); > +err_pm: > + pm_runtime_disable(dev->dev); > err_sram: > sunxi_sram_release(dev->dev); > err_mem: > @@ -282,11 +316,9 @@ int cedrus_hw_probe(struct cedrus_dev *dev) > > void cedrus_hw_remove(struct cedrus_dev *dev) > { > - reset_control_assert(dev->rstc); > - > - clk_disable_unprepare(dev->ram_clk); > - clk_disable_unprepare(dev->mod_clk); > - clk_disable_unprepare(dev->ahb_clk); > + pm_runtime_disable(dev->dev); > + if (!pm_runtime_status_suspended(dev->dev)) > + cedrus_hw_suspend(dev->dev); > > sunxi_sram_release(dev->dev); > > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h > index 604ff932fbf5..45f641f0bfa2 100644 > --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h > @@ -22,6 +22,9 @@ void cedrus_engine_disable(struct cedrus_dev *dev); > void cedrus_dst_format_set(struct cedrus_dev *dev, > struct v4l2_pix_format *fmt); > > +int cedrus_hw_suspend(struct device *device); > +int cedrus_hw_resume(struct device *device); > + > int cedrus_hw_probe(struct cedrus_dev *dev); > void cedrus_hw_remove(struct cedrus_dev *dev); > > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c > index ed3f511f066f..16d82309e7b6 100644 > --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c > @@ -13,6 +13,8 @@ > * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx> > */ > > +#include <linux/pm_runtime.h> > + > #include <media/videobuf2-dma-contig.h> > #include <media/v4l2-device.h> > #include <media/v4l2-ioctl.h> > @@ -450,12 +452,24 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) > return -EINVAL; > } > > - if (V4L2_TYPE_IS_OUTPUT(vq->type) && > - dev->dec_ops[ctx->current_codec]->start) > - ret = dev->dec_ops[ctx->current_codec]->start(ctx); > + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { > + ret = pm_runtime_get_sync(dev->dev); It's entirely up to you, but you could do get_sync/put between .device_run, as everything should happen in the context of v4l2_m2m_ops.device_run. (In that case, I believe you'd want to enable autosuspend.) Not sure there's much to gain from an power consumption pov, though. With the Originally-by changed: Reviewed-by: Ezequiel Garcia <ezequiel@xxxxxxxxxxxxx> Thanks, Ezequiel