Skip to content

Commit d5aecd2

Browse files
jernejskmchehab
authored andcommitted
media: cedrus: Implement runtime PM
This allows the VE clocks and PLL_VE to be disabled most of the time. A runtime PM reference is held while streaming. Signed-off-by: Jernej Skrabec <[email protected]> Signed-off-by: Samuel Holland <[email protected]> Reviewed-by: Ezequiel Garcia <[email protected]> Signed-off-by: Hans Verkuil <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent a8876c2 commit d5aecd2

File tree

4 files changed

+104
-45
lines changed

4 files changed

+104
-45
lines changed

drivers/staging/media/sunxi/cedrus/cedrus.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/platform_device.h>
1717
#include <linux/module.h>
1818
#include <linux/of.h>
19+
#include <linux/pm.h>
1920

2021
#include <media/v4l2-device.h>
2122
#include <media/v4l2-ioctl.h>
@@ -551,12 +552,18 @@ static const struct of_device_id cedrus_dt_match[] = {
551552
};
552553
MODULE_DEVICE_TABLE(of, cedrus_dt_match);
553554

555+
static const struct dev_pm_ops cedrus_dev_pm_ops = {
556+
SET_RUNTIME_PM_OPS(cedrus_hw_suspend,
557+
cedrus_hw_resume, NULL)
558+
};
559+
554560
static struct platform_driver cedrus_driver = {
555561
.probe = cedrus_probe,
556562
.remove = cedrus_remove,
557563
.driver = {
558564
.name = CEDRUS_NAME,
559565
.of_match_table = of_match_ptr(cedrus_dt_match),
566+
.pm = &cedrus_dev_pm_ops,
560567
},
561568
};
562569
module_platform_driver(cedrus_driver);

drivers/staging/media/sunxi/cedrus/cedrus_hw.c

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/dma-mapping.h>
2020
#include <linux/interrupt.h>
2121
#include <linux/clk.h>
22+
#include <linux/pm_runtime.h>
2223
#include <linux/regmap.h>
2324
#include <linux/reset.h>
2425
#include <linux/soc/sunxi/sunxi_sram.h>
@@ -140,6 +141,64 @@ static irqreturn_t cedrus_irq(int irq, void *data)
140141
return IRQ_HANDLED;
141142
}
142143

144+
int cedrus_hw_suspend(struct device *device)
145+
{
146+
struct cedrus_dev *dev = dev_get_drvdata(device);
147+
148+
reset_control_assert(dev->rstc);
149+
150+
clk_disable_unprepare(dev->ram_clk);
151+
clk_disable_unprepare(dev->mod_clk);
152+
clk_disable_unprepare(dev->ahb_clk);
153+
154+
return 0;
155+
}
156+
157+
int cedrus_hw_resume(struct device *device)
158+
{
159+
struct cedrus_dev *dev = dev_get_drvdata(device);
160+
int ret;
161+
162+
ret = clk_prepare_enable(dev->ahb_clk);
163+
if (ret) {
164+
dev_err(dev->dev, "Failed to enable AHB clock\n");
165+
166+
return ret;
167+
}
168+
169+
ret = clk_prepare_enable(dev->mod_clk);
170+
if (ret) {
171+
dev_err(dev->dev, "Failed to enable MOD clock\n");
172+
173+
goto err_ahb_clk;
174+
}
175+
176+
ret = clk_prepare_enable(dev->ram_clk);
177+
if (ret) {
178+
dev_err(dev->dev, "Failed to enable RAM clock\n");
179+
180+
goto err_mod_clk;
181+
}
182+
183+
ret = reset_control_reset(dev->rstc);
184+
if (ret) {
185+
dev_err(dev->dev, "Failed to apply reset\n");
186+
187+
goto err_ram_clk;
188+
}
189+
190+
return 0;
191+
192+
err_ram_clk:
193+
clk_disable_unprepare(dev->ram_clk);
194+
err_mod_clk:
195+
clk_disable_unprepare(dev->mod_clk);
196+
err_ahb_clk:
197+
clk_disable_unprepare(dev->ahb_clk);
198+
199+
return ret;
200+
}
201+
143202
int cedrus_hw_probe(struct cedrus_dev *dev)
144203
{
145204
const struct cedrus_variant *variant;
@@ -236,42 +295,17 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
236295
goto err_sram;
237296
}
238297

239-
ret = clk_prepare_enable(dev->ahb_clk);
240-
if (ret) {
241-
dev_err(dev->dev, "Failed to enable AHB clock\n");
242-
243-
goto err_sram;
244-
}
245-
246-
ret = clk_prepare_enable(dev->mod_clk);
247-
if (ret) {
248-
dev_err(dev->dev, "Failed to enable MOD clock\n");
249-
250-
goto err_ahb_clk;
251-
}
252-
253-
ret = clk_prepare_enable(dev->ram_clk);
254-
if (ret) {
255-
dev_err(dev->dev, "Failed to enable RAM clock\n");
256-
257-
goto err_mod_clk;
258-
}
259-
260-
ret = reset_control_reset(dev->rstc);
261-
if (ret) {
262-
dev_err(dev->dev, "Failed to apply reset\n");
263-
264-
goto err_ram_clk;
298+
pm_runtime_enable(dev->dev);
299+
if (!pm_runtime_enabled(dev->dev)) {
300+
ret = cedrus_hw_resume(dev->dev);
301+
if (ret)
302+
goto err_pm;
265303
}
266304

267305
return 0;
268306

269-
err_ram_clk:
270-
clk_disable_unprepare(dev->ram_clk);
271-
err_mod_clk:
272-
clk_disable_unprepare(dev->mod_clk);
273-
err_ahb_clk:
274-
clk_disable_unprepare(dev->ahb_clk);
307+
err_pm:
308+
pm_runtime_disable(dev->dev);
275309
err_sram:
276310
sunxi_sram_release(dev->dev);
277311
err_mem:
@@ -282,11 +316,9 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
282316

283317
void cedrus_hw_remove(struct cedrus_dev *dev)
284318
{
285-
reset_control_assert(dev->rstc);
286-
287-
clk_disable_unprepare(dev->ram_clk);
288-
clk_disable_unprepare(dev->mod_clk);
289-
clk_disable_unprepare(dev->ahb_clk);
319+
pm_runtime_disable(dev->dev);
320+
if (!pm_runtime_status_suspended(dev->dev))
321+
cedrus_hw_suspend(dev->dev);
290322

291323
sunxi_sram_release(dev->dev);
292324

drivers/staging/media/sunxi/cedrus/cedrus_hw.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ void cedrus_engine_disable(struct cedrus_dev *dev);
2222
void cedrus_dst_format_set(struct cedrus_dev *dev,
2323
struct v4l2_pix_format *fmt);
2424

25+
int cedrus_hw_suspend(struct device *device);
26+
int cedrus_hw_resume(struct device *device);
27+
2528
int cedrus_hw_probe(struct cedrus_dev *dev);
2629
void cedrus_hw_remove(struct cedrus_dev *dev);
2730

drivers/staging/media/sunxi/cedrus/cedrus_video.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* Marek Szyprowski, <[email protected]>
1414
*/
1515

16+
#include <linux/pm_runtime.h>
17+
1618
#include <media/videobuf2-dma-contig.h>
1719
#include <media/v4l2-device.h>
1820
#include <media/v4l2-ioctl.h>
@@ -450,12 +452,24 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count)
450452
return -EINVAL;
451453
}
452454

453-
if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
454-
dev->dec_ops[ctx->current_codec]->start)
455-
ret = dev->dec_ops[ctx->current_codec]->start(ctx);
455+
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
456+
ret = pm_runtime_get_sync(dev->dev);
457+
if (ret < 0)
458+
goto err_cleanup;
456459

457-
if (ret)
458-
cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
460+
if (dev->dec_ops[ctx->current_codec]->start) {
461+
ret = dev->dec_ops[ctx->current_codec]->start(ctx);
462+
if (ret)
463+
goto err_pm;
464+
}
465+
}
466+
467+
return 0;
468+
469+
err_pm:
470+
pm_runtime_put(dev->dev);
471+
err_cleanup:
472+
cedrus_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
459473

460474
return ret;
461475
}
@@ -465,9 +479,12 @@ static void cedrus_stop_streaming(struct vb2_queue *vq)
465479
struct cedrus_ctx *ctx = vb2_get_drv_priv(vq);
466480
struct cedrus_dev *dev = ctx->dev;
467481

468-
if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
469-
dev->dec_ops[ctx->current_codec]->stop)
470-
dev->dec_ops[ctx->current_codec]->stop(ctx);
482+
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
483+
if (dev->dec_ops[ctx->current_codec]->stop)
484+
dev->dec_ops[ctx->current_codec]->stop(ctx);
485+
486+
pm_runtime_put(dev->dev);
487+
}
471488

472489
cedrus_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
473490
}

0 commit comments

Comments
 (0)