Skip to content

Commit 50841ae

Browse files
committed
media/hevc_d: Add runtime and system suspend/resume PM support
Replace the manual clock enable/disable on stream start/stop with runtime PM, and add system sleep callbacks. The IRQ enables in the interrupt controller were only set at probe. Move them into the power on path so they are restored on every resume. On system suspend, stop new m2m jobs being run and then sync with the IRQ machinery. v4l2_m2m_suspend() only waits for the current job to finish, but jobs are finished before the h/w has completed phase 1 and phase 2, so that alone does not leave the h/w idle. Signed-off-by: John Cox <jc@kynesim.co.uk>
1 parent d80519f commit 50841ae

6 files changed

Lines changed: 163 additions & 41 deletions

File tree

‎drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c‎

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <linux/dma-mapping.h>
1515
#include <linux/platform_device.h>
16+
#include <linux/pm_runtime.h>
1617
#include <linux/module.h>
1718

1819
#include <media/v4l2-device.h>
@@ -220,6 +221,8 @@ static int hevc_d_probe(struct platform_device *pdev)
220221
dev->vfd = hevc_d_video_device;
221222
dev->dev = &pdev->dev;
222223
dev->pdev = pdev;
224+
/* Must be set before runtime PM is enabled in hevc_d_hw_probe */
225+
platform_set_drvdata(pdev, dev);
223226

224227
ret = hevc_d_hw_probe(dev);
225228
if (ret) {
@@ -290,8 +293,6 @@ static int hevc_d_probe(struct platform_device *pdev)
290293
goto err_m2m_mc;
291294
}
292295

293-
platform_set_drvdata(pdev, dev);
294-
295296
return 0;
296297

297298
err_m2m_mc:
@@ -326,6 +327,59 @@ static void hevc_d_remove(struct platform_device *pdev)
326327
hevc_d_hw_remove(dev);
327328
}
328329

330+
static int hevc_d_runtime_suspend(struct device *d)
331+
{
332+
struct hevc_d_dev *dev = dev_get_drvdata(d);
333+
334+
hevc_d_hw_power_off(dev);
335+
return 0;
336+
}
337+
338+
static int hevc_d_runtime_resume(struct device *d)
339+
{
340+
struct hevc_d_dev *dev = dev_get_drvdata(d);
341+
342+
return hevc_d_hw_power_on(dev);
343+
}
344+
345+
static int hevc_d_suspend(struct device *d)
346+
{
347+
struct hevc_d_dev *dev = dev_get_drvdata(d);
348+
int ret;
349+
350+
/* Stop new jobs being started */
351+
v4l2_m2m_suspend(dev->m2m_dev);
352+
/*
353+
* v4l2_m2m_suspend only waits for the current job to finish but we
354+
* finish jobs before the h/w is done with them so also wait for any
355+
* phase 1 / phase 2 work still in flight
356+
*/
357+
hevc_d_h265_sync(dev);
358+
359+
ret = pm_runtime_force_suspend(d);
360+
if (ret)
361+
v4l2_m2m_resume(dev->m2m_dev);
362+
return ret;
363+
}
364+
365+
static int hevc_d_resume(struct device *d)
366+
{
367+
struct hevc_d_dev *dev = dev_get_drvdata(d);
368+
int ret;
369+
370+
ret = pm_runtime_force_resume(d);
371+
if (ret)
372+
return ret;
373+
374+
v4l2_m2m_resume(dev->m2m_dev);
375+
return 0;
376+
}
377+
378+
static const struct dev_pm_ops hevc_d_pm_ops = {
379+
SYSTEM_SLEEP_PM_OPS(hevc_d_suspend, hevc_d_resume)
380+
RUNTIME_PM_OPS(hevc_d_runtime_suspend, hevc_d_runtime_resume, NULL)
381+
};
382+
329383
static const struct of_device_id hevc_d_dt_match[] = {
330384
{ .compatible = "brcm,bcm2711-hevc-dec", },
331385
{ .compatible = "brcm,bcm2712-hevc-dec", },
@@ -339,6 +393,7 @@ static struct platform_driver hevc_d_driver = {
339393
.driver = {
340394
.name = HEVC_D_NAME,
341395
.of_match_table = hevc_d_dt_match,
396+
.pm = pm_ptr(&hevc_d_pm_ops),
342397
},
343398
};
344399
module_platform_driver(hevc_d_driver);

‎drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,6 +2318,12 @@ static void h265_ctx_uninit(struct hevc_d_dev *const dev, struct hevc_d_ctx *ctx
23182318
ctx->coeff_size_max = 0;
23192319
}
23202320

2321+
/* Wait for all queued h/w operations on all contexts to complete */
2322+
void hevc_d_h265_sync(struct hevc_d_dev *dev)
2323+
{
2324+
irq_sync(dev);
2325+
}
2326+
23212327
void hevc_d_h265_stop(struct hevc_d_ctx *ctx)
23222328
{
23232329
struct hevc_d_dev *const dev = ctx->dev;

‎drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern const struct v4l2_ctrl_ops hevc_d_hevc_slice_params_ctrl_ops;
1717
int hevc_d_h265_start(struct hevc_d_ctx *ctx);
1818
void hevc_d_h265_stop(struct hevc_d_ctx *ctx);
1919
void hevc_d_h265_trigger(struct hevc_d_ctx *ctx);
20+
void hevc_d_h265_sync(struct hevc_d_dev *dev);
2021

2122
void hevc_d_device_run(void *priv);
2223

‎drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.c‎

Lines changed: 90 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/clk.h>
1414
#include <linux/interrupt.h>
1515
#include <linux/platform_device.h>
16+
#include <linux/pm_runtime.h>
1617

1718
#include <media/videobuf2-core.h>
1819
#include <media/v4l2-mem2mem.h>
@@ -300,20 +301,26 @@ void hevc_d_hw_irq_active2_irq(struct hevc_d_dev *dev,
300301
}
301302

302303
/*
303-
* Stop the clock for this context
304-
* clk_disable_unprepare does ref counting so this will not actually
305-
* disable the clock if there are other running contexts
304+
* Power down the h/w
305+
* Called from runtime suspend (or directly if runtime PM is unavailable)
306306
*/
307-
void hevc_d_hw_stop_clock(struct hevc_d_dev *dev)
307+
void hevc_d_hw_power_off(struct hevc_d_dev *dev)
308308
{
309+
dev_dbg(dev->dev, "Power (clock) off\n");
309310
clk_disable_unprepare(dev->clock);
310311
}
311312

312-
/* Always starts the clock if it isn't already on this ctx */
313-
int hevc_d_hw_start_clock(struct hevc_d_dev *dev)
313+
/*
314+
* Power up the h/w and (re)initialise any state that is not set per-job
315+
* Called from runtime resume (or directly if runtime PM is unavailable)
316+
*/
317+
int hevc_d_hw_power_on(struct hevc_d_dev *dev)
314318
{
319+
u32 irq_stat;
315320
int rv;
316321

322+
dev_dbg(dev->dev, "Power (clock) on\n");
323+
317324
rv = clk_set_min_rate(dev->clock, dev->max_clock_rate);
318325
if (rv) {
319326
dev_err(dev->dev, "Failed to set clock rate\n");
@@ -325,22 +332,47 @@ int hevc_d_hw_start_clock(struct hevc_d_dev *dev)
325332
dev_err(dev->dev, "Failed to enable clock\n");
326333
return rv;
327334
}
335+
336+
/*
337+
* Enable IRQs & reset anything pending
338+
* Whilst this seems the wrong way round the h/w doesn't actually
339+
* set the IRQ status bits till the IRQs are enabled. On probe we
340+
* haven't got the IRQ yet and on resume nothing is in flight so
341+
* this should still be safe.
342+
*/
343+
irq_write(dev, ARG_IC_ICTRL,
344+
ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
345+
irq_stat = irq_read(dev, ARG_IC_ICTRL);
346+
irq_write(dev, ARG_IC_ICTRL, irq_stat);
347+
328348
return 0;
329349
}
330350

331-
static int hw_setup(struct hevc_d_dev *dev)
351+
/*
352+
* Take a power reference for this context
353+
* Runtime PM does ref counting so the h/w stays powered whilst any
354+
* context holds a reference
355+
*/
356+
int hevc_d_hw_pm_get(struct hevc_d_dev *dev)
357+
{
358+
int rv;
359+
360+
rv = pm_runtime_resume_and_get(dev->dev);
361+
if (rv)
362+
dev_err(dev->dev, "Failed to power up: %d\n", rv);
363+
return rv;
364+
}
365+
366+
void hevc_d_hw_pm_put(struct hevc_d_dev *dev)
367+
{
368+
pm_runtime_put(dev->dev);
369+
}
370+
371+
static int get_max_clock_rate(struct hevc_d_dev *dev)
332372
{
333373
struct device_node *node;
334-
u32 ver;
335-
u32 irq_stat;
336374
struct rpi_firmware *firmware;
337375

338-
ver = apb_read(dev, RPI_VERSION);
339-
if (ver != 0x202) {
340-
dev_err(dev->dev, "Unexpected version %#x only 0x202 supported\n", ver);
341-
return -ENODEV;
342-
}
343-
344376
node = rpi_firmware_find_node();
345377
if (!node)
346378
return -EINVAL;
@@ -354,17 +386,19 @@ static int hw_setup(struct hevc_d_dev *dev)
354386
RPI_FIRMWARE_HEVC_CLK_ID);
355387
rpi_firmware_put(firmware);
356388

357-
/*
358-
* Enable IRQs & reset anything pending
359-
* Whilst this seems the wrong way round the h/w doesn't actually
360-
* set the IRQ status bits till the IRQs are enabled. As we haven't
361-
* got the IRQ yet this should still be safe.
362-
*/
363-
irq_write(dev, ARG_IC_ICTRL,
364-
ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
365-
irq_stat = irq_read(dev, ARG_IC_ICTRL);
366-
irq_write(dev, ARG_IC_ICTRL, irq_stat);
389+
return 0;
390+
}
391+
392+
/* H/w must be powered */
393+
static int check_version(struct hevc_d_dev *dev)
394+
{
395+
u32 ver;
367396

397+
ver = apb_read(dev, RPI_VERSION);
398+
if (ver != 0x202) {
399+
dev_err(dev->dev, "Unexpected version %#x only 0x202 supported\n", ver);
400+
return -ENODEV;
401+
}
368402
return 0;
369403
}
370404

@@ -388,34 +422,58 @@ int hevc_d_hw_probe(struct hevc_d_dev *dev)
388422
if (IS_ERR(dev->clock))
389423
return PTR_ERR(dev->clock);
390424

391-
ret = clk_prepare_enable(dev->clock);
425+
ret = get_max_clock_rate(dev);
392426
if (ret)
393427
return ret;
394-
ret = hw_setup(dev);
395-
clk_disable_unprepare(dev->clock);
428+
429+
/*
430+
* Power up manually so this works without runtime PM. Runtime PM is
431+
* told we are active once enabled and the driver core idles us after
432+
* probe completes.
433+
*/
434+
ret = hevc_d_hw_power_on(dev);
396435
if (ret)
397436
return ret;
398437

438+
ret = check_version(dev);
439+
if (ret)
440+
goto fail_power_off;
441+
399442
irq_dec = platform_get_irq(dev->pdev, 0);
400-
if (irq_dec < 0)
401-
return irq_dec;
443+
if (irq_dec < 0) {
444+
ret = irq_dec;
445+
goto fail_power_off;
446+
}
402447
ret = devm_request_threaded_irq(dev->dev, irq_dec,
403448
hevc_d_irq_irq,
404449
hevc_d_irq_thread,
405450
0, dev_name(dev->dev), dev);
406-
if (ret)
451+
if (ret) {
407452
dev_err(dev->dev, "Failed to request IRQ - %d\n", ret);
453+
goto fail_power_off;
454+
}
455+
456+
pm_runtime_set_active(dev->dev);
457+
pm_runtime_enable(dev->dev);
408458

459+
return 0;
460+
461+
fail_power_off:
462+
hevc_d_hw_power_off(dev);
409463
return ret;
410464
}
411465

412466
void hevc_d_hw_remove(struct hevc_d_dev *dev)
413467
{
468+
pm_runtime_disable(dev->dev);
469+
if (!pm_runtime_status_suspended(dev->dev))
470+
hevc_d_hw_power_off(dev);
471+
pm_runtime_set_suspended(dev->dev);
472+
414473
/*
415474
* IRQ auto freed on unload so no need to do it here
416475
* ioremap auto freed on unload
417476
*/
418477
ictl_uninit(&dev->ic_active1);
419478
ictl_uninit(&dev->ic_active2);
420479
}
421-

‎drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,10 @@ void hevc_d_hw_irq_active2_irq(struct hevc_d_dev *dev,
312312
struct hevc_d_hw_irq_ent *ient,
313313
hevc_d_irq_callback irq_cb, void *ctx);
314314

315-
int hevc_d_hw_start_clock(struct hevc_d_dev *dev);
316-
void hevc_d_hw_stop_clock(struct hevc_d_dev *dev);
315+
int hevc_d_hw_power_on(struct hevc_d_dev *dev);
316+
void hevc_d_hw_power_off(struct hevc_d_dev *dev);
317+
int hevc_d_hw_pm_get(struct hevc_d_dev *dev);
318+
void hevc_d_hw_pm_put(struct hevc_d_dev *dev);
317319

318320
int hevc_d_hw_probe(struct hevc_d_dev *dev);
319321
void hevc_d_hw_remove(struct hevc_d_dev *dev);

‎drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -535,19 +535,19 @@ static int hevc_d_start_streaming(struct vb2_queue *vq, unsigned int count)
535535
v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, vq);
536536

537537
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
538-
ret = hevc_d_hw_start_clock(dev);
538+
ret = hevc_d_hw_pm_get(dev);
539539
if (ret)
540540
goto fail_cleanup;
541541

542542
ret = hevc_d_h265_start(ctx);
543543
if (ret)
544-
goto fail_stop_clock;
544+
goto fail_pm_put;
545545
}
546546

547547
return 0;
548548

549-
fail_stop_clock:
550-
hevc_d_hw_stop_clock(dev);
549+
fail_pm_put:
550+
hevc_d_hw_pm_put(dev);
551551
fail_cleanup:
552552
v4l2_err(&dev->v4l2_dev, "%s: qtype=%d: FAIL\n", __func__, vq->type);
553553
hevc_d_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
@@ -561,7 +561,7 @@ static void hevc_d_stop_streaming(struct vb2_queue *vq)
561561

562562
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
563563
hevc_d_h265_stop(ctx);
564-
hevc_d_hw_stop_clock(dev);
564+
hevc_d_hw_pm_put(dev);
565565
}
566566

567567
hevc_d_queue_cleanup(vq, VB2_BUF_STATE_ERROR);

0 commit comments

Comments
 (0)