diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c index 392fd40119fcfb..3a4c192357f6e0 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -46,7 +47,7 @@ static const struct v4l2_ctrl_config hevc_d_ctrls[] = { .id = V4L2_CID_STATELESS_HEVC_PPS, .ops = &hevc_d_hevc_pps_ctrl_ops, }, { - .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, }, { .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, }, { @@ -66,7 +67,17 @@ static const struct v4l2_ctrl_config hevc_d_ctrls[] = { .min = V4L2_STATELESS_HEVC_START_CODE_NONE, .max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, .def = V4L2_STATELESS_HEVC_START_CODE_NONE, - }, + }, { + .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + }, { + .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, + .def = V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1, + } }; void *hevc_d_find_control_data(struct hevc_d_ctx *ctx, u32 id) @@ -211,6 +222,7 @@ static int hevc_d_probe(struct platform_device *pdev) dev->vfd = hevc_d_video_device; dev->dev = &pdev->dev; dev->pdev = pdev; + platform_set_drvdata(pdev, dev); ret = hevc_d_hw_probe(dev); if (ret) { @@ -281,8 +293,6 @@ static int hevc_d_probe(struct platform_device *pdev) goto err_m2m_mc; } - platform_set_drvdata(pdev, dev); - return 0; err_m2m_mc: @@ -317,6 +327,59 @@ static void hevc_d_remove(struct platform_device *pdev) hevc_d_hw_remove(dev); } +static int hevc_d_runtime_suspend(struct device *d) +{ + struct hevc_d_dev *dev = dev_get_drvdata(d); + + hevc_d_hw_power_off(dev); + return 0; +} + +static int hevc_d_runtime_resume(struct device *d) +{ + struct hevc_d_dev *dev = dev_get_drvdata(d); + + return hevc_d_hw_power_on(dev); +} + +static int hevc_d_suspend(struct device *d) +{ + struct hevc_d_dev *dev = dev_get_drvdata(d); + int ret; + + /* Stop new jobs being started */ + v4l2_m2m_suspend(dev->m2m_dev); + /* + * v4l2_m2m_suspend only waits for the current job to finish but we + * finish jobs before the h/w is done with them so also wait for any + * phase 1 / phase 2 work still in flight + */ + hevc_d_h265_sync(dev); + + ret = pm_runtime_force_suspend(d); + if (ret) + v4l2_m2m_resume(dev->m2m_dev); + return ret; +} + +static int hevc_d_resume(struct device *d) +{ + struct hevc_d_dev *dev = dev_get_drvdata(d); + int ret; + + ret = pm_runtime_force_resume(d); + if (ret) + return ret; + + v4l2_m2m_resume(dev->m2m_dev); + return 0; +} + +static const struct dev_pm_ops hevc_d_pm_ops = { + SYSTEM_SLEEP_PM_OPS(hevc_d_suspend, hevc_d_resume) + RUNTIME_PM_OPS(hevc_d_runtime_suspend, hevc_d_runtime_resume, NULL) +}; + static const struct of_device_id hevc_d_dt_match[] = { { .compatible = "brcm,bcm2711-hevc-dec", }, { .compatible = "brcm,bcm2712-hevc-dec", }, @@ -330,6 +393,7 @@ static struct platform_driver hevc_d_driver = { .driver = { .name = HEVC_D_NAME, .of_match_table = hevc_d_dt_match, + .pm = pm_ptr(&hevc_d_pm_ops), }, }; module_platform_driver(hevc_d_driver); diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c index 8be35a9d8dfd10..07d171feddfb29 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c @@ -2286,7 +2286,7 @@ static void phase1_sync_claimed(struct hevc_d_dev *const dev, void *v) * phase1 has counted enables so must reenable once claimed * phase2 has unlimited enables */ -static void irq_sync(struct hevc_d_dev *const dev) +void hevc_d_h265_sync(struct hevc_d_dev *dev) { struct irq_sync sync; @@ -2322,7 +2322,7 @@ void hevc_d_h265_stop(struct hevc_d_ctx *ctx) { struct hevc_d_dev *const dev = ctx->dev; - irq_sync(dev); + hevc_d_h265_sync(dev); h265_ctx_uninit(dev, ctx); } diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h index 9961917bf20401..75c248c76da668 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h @@ -17,6 +17,7 @@ extern const struct v4l2_ctrl_ops hevc_d_hevc_slice_params_ctrl_ops; int hevc_d_h265_start(struct hevc_d_ctx *ctx); void hevc_d_h265_stop(struct hevc_d_ctx *ctx); void hevc_d_h265_trigger(struct hevc_d_ctx *ctx); +void hevc_d_h265_sync(struct hevc_d_dev *dev); void hevc_d_device_run(void *priv); diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.c index 8564b332f8d2af..14098fe0b57027 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -300,18 +301,22 @@ void hevc_d_hw_irq_active2_irq(struct hevc_d_dev *dev, } /* - * Stop the clock for this context - * clk_disable_unprepare does ref counting so this will not actually - * disable the clock if there are other running contexts + * Power down the h/w + * Called from runtime suspend (or directly if runtime PM is unavailable) */ -void hevc_d_hw_stop_clock(struct hevc_d_dev *dev) +void hevc_d_hw_power_off(struct hevc_d_dev *dev) { + clk_set_min_rate(dev->clock, 0); clk_disable_unprepare(dev->clock); } -/* Always starts the clock if it isn't already on this ctx */ -int hevc_d_hw_start_clock(struct hevc_d_dev *dev) +/* + * Power up the h/w and (re)initialise any state that is not set per-job + * Called from runtime resume (or directly if runtime PM is unavailable) + */ +int hevc_d_hw_power_on(struct hevc_d_dev *dev) { + u32 irq_stat; int rv; rv = clk_set_min_rate(dev->clock, dev->max_clock_rate); @@ -325,22 +330,47 @@ int hevc_d_hw_start_clock(struct hevc_d_dev *dev) dev_err(dev->dev, "Failed to enable clock\n"); return rv; } + + /* + * Enable IRQs & reset anything pending + * Whilst this seems the wrong way round the h/w doesn't actually + * set the IRQ status bits till the IRQs are enabled. On probe we + * haven't got the IRQ yet and on resume nothing is in flight so + * this should still be safe. + */ + irq_write(dev, ARG_IC_ICTRL, + ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET); + irq_stat = irq_read(dev, ARG_IC_ICTRL); + irq_write(dev, ARG_IC_ICTRL, irq_stat); + return 0; } -static int hw_setup(struct hevc_d_dev *dev) +/* + * Take a power reference for this context + * Runtime PM does ref counting so the h/w stays powered whilst any + * context holds a reference + */ +int hevc_d_hw_pm_get(struct hevc_d_dev *dev) +{ + int rv; + + rv = pm_runtime_resume_and_get(dev->dev); + if (rv) + dev_err(dev->dev, "Failed to power up: %d\n", rv); + return rv; +} + +void hevc_d_hw_pm_put(struct hevc_d_dev *dev) +{ + pm_runtime_put(dev->dev); +} + +static int get_max_clock_rate(struct hevc_d_dev *dev) { struct device_node *node; - u32 ver; - u32 irq_stat; struct rpi_firmware *firmware; - ver = apb_read(dev, RPI_VERSION); - if (ver != 0x202) { - dev_err(dev->dev, "Unexpected version %#x only 0x202 supported\n", ver); - return -ENODEV; - } - node = rpi_firmware_find_node(); if (!node) return -EINVAL; @@ -354,17 +384,19 @@ static int hw_setup(struct hevc_d_dev *dev) RPI_FIRMWARE_HEVC_CLK_ID); rpi_firmware_put(firmware); - /* - * Enable IRQs & reset anything pending - * Whilst this seems the wrong way round the h/w doesn't actually - * set the IRQ status bits till the IRQs are enabled. As we haven't - * got the IRQ yet this should still be safe. - */ - irq_write(dev, ARG_IC_ICTRL, - ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET); - irq_stat = irq_read(dev, ARG_IC_ICTRL); - irq_write(dev, ARG_IC_ICTRL, irq_stat); + return 0; +} + +/* H/w must be powered */ +static int check_version(struct hevc_d_dev *dev) +{ + u32 ver; + ver = apb_read(dev, RPI_VERSION); + if (ver != 0x202) { + dev_err(dev->dev, "Unexpected version %#x only 0x202 supported\n", ver); + return -ENODEV; + } return 0; } @@ -388,29 +420,54 @@ int hevc_d_hw_probe(struct hevc_d_dev *dev) if (IS_ERR(dev->clock)) return PTR_ERR(dev->clock); - ret = clk_prepare_enable(dev->clock); + ret = get_max_clock_rate(dev); if (ret) return ret; - ret = hw_setup(dev); - clk_disable_unprepare(dev->clock); + + /* + * Power up manually so this works without runtime PM. Runtime PM is + * told we are active once enabled and the driver core idles us after + * probe completes. + */ + ret = hevc_d_hw_power_on(dev); if (ret) return ret; + ret = check_version(dev); + if (ret) + goto fail_power_off; + irq_dec = platform_get_irq(dev->pdev, 0); - if (irq_dec < 0) - return irq_dec; + if (irq_dec < 0) { + ret = irq_dec; + goto fail_power_off; + } ret = devm_request_threaded_irq(dev->dev, irq_dec, hevc_d_irq_irq, hevc_d_irq_thread, 0, dev_name(dev->dev), dev); - if (ret) + if (ret) { dev_err(dev->dev, "Failed to request IRQ - %d\n", ret); + goto fail_power_off; + } + pm_runtime_set_active(dev->dev); + pm_runtime_enable(dev->dev); + + return 0; + +fail_power_off: + hevc_d_hw_power_off(dev); return ret; } void hevc_d_hw_remove(struct hevc_d_dev *dev) { + pm_runtime_disable(dev->dev); + if (!pm_runtime_status_suspended(dev->dev)) + hevc_d_hw_power_off(dev); + pm_runtime_set_suspended(dev->dev); + /* * IRQ auto freed on unload so no need to do it here * ioremap auto freed on unload @@ -418,4 +475,3 @@ void hevc_d_hw_remove(struct hevc_d_dev *dev) ictl_uninit(&dev->ic_active1); ictl_uninit(&dev->ic_active2); } - diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.h b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.h index 85fe96fc13d4c5..fae4474d00a3b1 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.h +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_hw.h @@ -312,8 +312,10 @@ void hevc_d_hw_irq_active2_irq(struct hevc_d_dev *dev, struct hevc_d_hw_irq_ent *ient, hevc_d_irq_callback irq_cb, void *ctx); -int hevc_d_hw_start_clock(struct hevc_d_dev *dev); -void hevc_d_hw_stop_clock(struct hevc_d_dev *dev); +int hevc_d_hw_power_on(struct hevc_d_dev *dev); +void hevc_d_hw_power_off(struct hevc_d_dev *dev); +int hevc_d_hw_pm_get(struct hevc_d_dev *dev); +void hevc_d_hw_pm_put(struct hevc_d_dev *dev); int hevc_d_hw_probe(struct hevc_d_dev *dev); void hevc_d_hw_remove(struct hevc_d_dev *dev); diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c index 27e92ccad24dfa..43609a66da6c9f 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c @@ -194,6 +194,26 @@ static int hevc_d_querycap(struct file *file, void *priv, return 0; } +static int hevc_d_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index != 0) + return -EINVAL; + + if (fsize->pixel_format != V4L2_PIX_FMT_HEVC_SLICE) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = HEVC_D_MIN_WIDTH; + fsize->stepwise.max_width = HEVC_D_MAX_WIDTH; + fsize->stepwise.step_width = 4; + fsize->stepwise.min_height = HEVC_D_MIN_HEIGHT; + fsize->stepwise.max_height = HEVC_D_MAX_HEIGHT; + fsize->stepwise.step_height = 4; + + return 0; +} + static int hevc_d_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -385,7 +405,7 @@ static int hevc_d_s_fmt_vid_out(struct file *file, void *priv, int ret; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (vb2_is_busy(vq)) + if (vb2_is_streaming(vq)) return -EBUSY; ret = hevc_d_try_fmt_vid_out(file, priv, f); @@ -402,6 +422,7 @@ static int hevc_d_s_fmt_vid_out(struct file *file, void *priv, const struct v4l2_ioctl_ops hevc_d_ioctl_ops = { .vidioc_querycap = hevc_d_querycap, + .vidioc_enum_framesizes = hevc_d_enum_framesizes, .vidioc_enum_fmt_vid_cap = hevc_d_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap_mplane = hevc_d_g_fmt_vid_cap, @@ -419,6 +440,7 @@ const struct v4l2_ioctl_ops hevc_d_ioctl_ops = { .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs, .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, .vidioc_streamon = v4l2_m2m_ioctl_streamon, @@ -513,19 +535,19 @@ static int hevc_d_start_streaming(struct vb2_queue *vq, unsigned int count) v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, vq); if (V4L2_TYPE_IS_OUTPUT(vq->type)) { - ret = hevc_d_hw_start_clock(dev); + ret = hevc_d_hw_pm_get(dev); if (ret) goto fail_cleanup; ret = hevc_d_h265_start(ctx); if (ret) - goto fail_stop_clock; + goto fail_pm_put; } return 0; -fail_stop_clock: - hevc_d_hw_stop_clock(dev); +fail_pm_put: + hevc_d_hw_pm_put(dev); fail_cleanup: v4l2_err(&dev->v4l2_dev, "%s: qtype=%d: FAIL\n", __func__, vq->type); hevc_d_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); @@ -539,7 +561,7 @@ static void hevc_d_stop_streaming(struct vb2_queue *vq) if (V4L2_TYPE_IS_OUTPUT(vq->type)) { hevc_d_h265_stop(ctx); - hevc_d_hw_stop_clock(dev); + hevc_d_hw_pm_put(dev); } hevc_d_queue_cleanup(vq, VB2_BUF_STATE_ERROR);