From 62ff53ded5c54f5aaff3f46754e9720bb0e0cad6 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 13 Jan 2026 21:20:42 +0800 Subject: [PATCH] media: platform: amd: isp4: support suspend/resume using runtime PM This work enables the suspend/resume feature of the ISP4. The major changes includes: 1. Support suspend/resume. 2. The power is managed by the runtime PM so the s_power and the related    callback function were dropped. 3. The enable_isp GPIO pin is controlled by the runtime PM. 4. pm_runtime_get_noresume() is used to get the runtime PM at probe()    since the device doesn't have to be set to power on when initialising. Signed-off-by: Kate Hsuan --- drivers/media/platform/amd/isp4/isp4.c | 55 ++++++++++++++++++- drivers/media/platform/amd/isp4/isp4_subdev.c | 48 +++++++++------- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index f35bc8f1a259..87a07b59356f 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -160,10 +160,13 @@ static int isp4_capture_probe(struct platform_device *pdev) goto err_clean_media; } - pm_runtime_set_suspended(dev); - pm_runtime_enable(dev); spin_lock_init(&isp_subdev->irq_lock); ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq); + + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + if (ret) { dev_err_probe(dev, ret, "fail init isp4 sub dev\n"); goto err_pm_disable; @@ -188,6 +191,10 @@ static int isp4_capture_probe(struct platform_device *pdev) platform_set_drvdata(pdev, isp_dev); isp_debugfs_create(isp_dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put_autosuspend(dev); + return 0; err_isp4_deinit: @@ -201,6 +208,42 @@ static int isp4_capture_probe(struct platform_device *pdev) return ret; } +static int isp4_suspend(struct device *dev) +{ + struct isp4_device *isp_dev = dev_get_drvdata(dev); + struct isp4_subdev *isp_subdev = &isp_dev->isp_subdev; + int ret = 0; + dev_dbg(dev, "ISP4 power off\n"); + + v4l2_subdev_disable_streams (&isp_subdev->sdev, isp_subdev->isp_vdev.vdev_pad.index, BIT(0)); + ret = gpiod_set_value(isp_subdev->enable_gpio, 0); + if (ret) { + dev_err(dev, "fail to set enable_isp gpio\n"); + return ret; + } + + return 0; +} + +static int isp4_resume(struct device *dev) +{ + struct isp4_device *isp_dev = dev_get_drvdata(dev); + struct isp4_subdev *isp_subdev = &isp_dev->isp_subdev; + int ret; + dev_dbg(dev, "ISP4 power on \n"); + + ret = gpiod_set_value(isp_subdev->enable_gpio, 1); + if (ret) { + dev_err(dev, "fail to set enable_isp gpio\n"); + return ret; + } + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(isp4_pm_ops, isp4_suspend, + isp4_resume, NULL); + static void isp4_capture_remove(struct platform_device *pdev) { struct isp4_device *isp_dev = platform_get_drvdata(pdev); @@ -213,6 +256,13 @@ static void isp4_capture_remove(struct platform_device *pdev) v4l2_device_unregister(&isp_dev->v4l2_dev); media_device_cleanup(&isp_dev->mdev); pm_runtime_disable(dev); + + /* + * Ensure the power is off before removing the device. + */ + if (!pm_runtime_status_suspended(dev)) + isp4_suspend(dev); + pm_runtime_set_suspended(dev); } static struct platform_driver isp4_capture_drv = { @@ -220,6 +270,7 @@ static struct platform_driver isp4_capture_drv = { .remove = isp4_capture_remove, .driver = { .name = ISP4_DRV_NAME, + .pm = pm_sleep_ptr(&isp4_pm_ops), } }; diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 2612ca283fc0..d53f2ab66783 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -730,6 +730,12 @@ static int isp4sd_pwron_and_init(struct isp4_subdev *isp_subdev) enable_irq(isp_subdev->irq[i]); isp_subdev->irq_enabled = true; + /* + * Hardware requires at least a 20ms delay between disabling and enabling the module, + * so a sleep is added to ensure ISP stability during quick reopen scenarios. + */ + msleep(20); + return 0; err_deinit: isp4sd_pwroff_and_deinit(isp_subdev); @@ -748,6 +754,11 @@ static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev, guard(mutex)(&isp_subdev->ops_mutex); dev_dbg(dev, "status %i\n", output_info->start_status); + if (output_info->start_status == ISP4SD_START_STATUS_OFF) { + dev_dbg(dev, "stream already stopped, do nothing\n"); + return 0; + } + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) { struct isp4fw_cmd_enable_out_ch cmd_ch_disable; @@ -781,6 +792,14 @@ static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev, isp4sd_uninit_stream(isp_subdev, state, pad); + isp4sd_pwroff_and_deinit(isp_subdev); + + ret = pm_runtime_put(isp_subdev->sdev.dev); + if (ret) { + dev_err(dev, "fail on pm_runtime_put\n"); + return ret; + } + /* * Return success to ensure the stop process proceeds, * and disregard any errors since they are not fatal. @@ -799,9 +818,16 @@ static int isp4sd_start_stream(struct isp4_subdev *isp_subdev, guard(mutex)(&isp_subdev->ops_mutex); - if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { - dev_err(dev, "fail, bad fsm %d", ispif->status); - return -EINVAL; + ret = pm_runtime_resume_and_get(isp_subdev->sdev.dev); + if (ret) { + dev_err(dev, "fail to get runtime pm\n"); + return ret; + } + + ret = isp4sd_pwron_and_init(isp_subdev); + if (ret) { + dev_err(dev, "fail to power on isp_subdev ret %d\n", ret); + return ret; } switch (output_info->start_status) { @@ -894,21 +920,6 @@ static int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, return ret; } -static int isp4sd_set_power(struct v4l2_subdev *sd, int on) -{ - struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); - - guard(mutex)(&isp_subdev->ops_mutex); - if (on) - return isp4sd_pwron_and_init(isp_subdev); - else - return isp4sd_pwroff_and_deinit(isp_subdev); -} - -static const struct v4l2_subdev_core_ops isp4sd_core_ops = { - .s_power = isp4sd_set_power, -}; - static const struct v4l2_subdev_video_ops isp4sd_video_ops = { .s_stream = v4l2_subdev_s_stream_helper, }; @@ -979,7 +990,6 @@ static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = { }; static const struct v4l2_subdev_ops isp4sd_subdev_ops = { - .core = &isp4sd_core_ops, .video = &isp4sd_video_ops, .pad = &isp4sd_pad_ops, }; -- 2.52.0