linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX
@ 2025-02-24 13:35 Jai Luthra
  2025-02-24 13:35 ` [PATCH 1/3] media: cadence: csi2rx: Support runtime PM Jai Luthra
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Jai Luthra @ 2025-02-24 13:35 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Maxime Ripard, Philipp Zabel, Sakari Ailus
  Cc: linux-media, linux-kernel, Laurent Pinchart, Tomi Valkeinen,
	Changhuang Liang, Devarsh Thakkar, Jai Luthra

This series adds support for runtime power management and system sleep
for the TI (J721E) CSI2RX pipeline.

Some basic testing with suspend-to-idle mode has been performed to
ensure that any ongoing stream resumes smoothly after wakeup. More
testing with suspend-to-memory mode will be done later, as I need to
update the device management boot firmware, which supports newer APIs
used by the mainline kernel.

I have picked PATCH 1/3 from Changhuang's earlier series [1] with minor
modifications.

Link: https://lore.kernel.org/linux-media/20240718032834.53876-1-changhuang.liang@starfivetech.com/ [1]

Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
Changhuang Liang (1):
      media: cadence: csi2rx: Support runtime PM

Jai Luthra (2):
      media: ti: j721e-csi2rx: Support runtime suspend
      media: ti: j721e-csi2rx: Support system suspend using pm_notifier

 drivers/media/platform/cadence/Kconfig             |   1 +
 drivers/media/platform/cadence/cdns-csi2rx.c       | 121 +++++++++-----
 drivers/media/platform/ti/Kconfig                  |   1 +
 .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c  | 180 +++++++++++++++++++--
 4 files changed, 252 insertions(+), 51 deletions(-)
---
base-commit: d082ecbc71e9e0bf49883ee4afd435a77a5101b6
change-id: 20250224-ti_csi_pm-24808b2b445c

Best regards,
-- 
Jai Luthra <jai.luthra@ideasonboard.com>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] media: cadence: csi2rx: Support runtime PM
  2025-02-24 13:35 [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Jai Luthra
@ 2025-02-24 13:35 ` Jai Luthra
  2025-02-24 13:35 ` [PATCH 2/3] media: ti: j721e-csi2rx: Support runtime suspend Jai Luthra
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Jai Luthra @ 2025-02-24 13:35 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Maxime Ripard, Philipp Zabel, Sakari Ailus
  Cc: linux-media, linux-kernel, Laurent Pinchart, Tomi Valkeinen,
	Changhuang Liang, Devarsh Thakkar, Jai Luthra

From: Changhuang Liang <changhuang.liang@starfivetech.com>

Use runtime power management hooks to save power when CSI-RX is not in
use.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
 drivers/media/platform/cadence/Kconfig       |   1 +
 drivers/media/platform/cadence/cdns-csi2rx.c | 121 ++++++++++++++++++---------
 2 files changed, 81 insertions(+), 41 deletions(-)

diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig
index 1aa608c00dbce9b52d0d48b5ac8c877db7494fd7..ea85ef82760e694c1e03fa323fc7a468b135e2e1 100644
--- a/drivers/media/platform/cadence/Kconfig
+++ b/drivers/media/platform/cadence/Kconfig
@@ -5,6 +5,7 @@ comment "Cadence media platform drivers"
 config VIDEO_CADENCE_CSI2RX
 	tristate "Cadence MIPI-CSI2 RX Controller"
 	depends on VIDEO_DEV
+	depends on PM
 	select MEDIA_CONTROLLER
 	select VIDEO_V4L2_SUBDEV_API
 	select V4L2_FWNODE
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 4d64df829e7585b6e305651f3ff9bdd008508ec5..44d3189fbec11d391352d4630326d78962b49a0e 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -211,11 +211,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 	u32 reg;
 	int ret;
 
-	ret = clk_prepare_enable(csi2rx->p_clk);
-	if (ret)
-		return ret;
-
-	reset_control_deassert(csi2rx->p_rst);
 	csi2rx_reset(csi2rx);
 
 	reg = csi2rx->num_lanes << 8;
@@ -253,7 +248,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 		if (ret) {
 			dev_err(csi2rx->dev,
 				"Failed to configure external DPHY: %d\n", ret);
-			goto err_disable_pclk;
+			return ret;
 		}
 	}
 
@@ -268,11 +263,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 	 * hence the reference counting.
 	 */
 	for (i = 0; i < csi2rx->max_streams; i++) {
-		ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
-		if (ret)
-			goto err_disable_pixclk;
-
-		reset_control_deassert(csi2rx->pixel_rst[i]);
 
 		writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
 		       csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
@@ -288,34 +278,18 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
 		       csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
 	}
 
-	ret = clk_prepare_enable(csi2rx->sys_clk);
-	if (ret)
-		goto err_disable_pixclk;
-
-	reset_control_deassert(csi2rx->sys_rst);
 
 	ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
 	if (ret)
-		goto err_disable_sysclk;
-
-	clk_disable_unprepare(csi2rx->p_clk);
+		goto err_phy_power_off;
 
 	return 0;
 
-err_disable_sysclk:
-	clk_disable_unprepare(csi2rx->sys_clk);
-err_disable_pixclk:
-	for (; i > 0; i--) {
-		reset_control_assert(csi2rx->pixel_rst[i - 1]);
-		clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
-	}
-
+err_phy_power_off:
 	if (csi2rx->dphy) {
 		writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
 		phy_power_off(csi2rx->dphy);
 	}
-err_disable_pclk:
-	clk_disable_unprepare(csi2rx->p_clk);
 
 	return ret;
 }
@@ -326,10 +300,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
 	u32 val;
 	int ret;
 
-	clk_prepare_enable(csi2rx->p_clk);
-	reset_control_assert(csi2rx->sys_rst);
-	clk_disable_unprepare(csi2rx->sys_clk);
-
 	for (i = 0; i < csi2rx->max_streams; i++) {
 		writel(CSI2RX_STREAM_CTRL_STOP,
 		       csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
@@ -342,14 +312,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
 		if (ret)
 			dev_warn(csi2rx->dev,
 				 "Failed to stop streaming on pad%u\n", i);
-
-		reset_control_assert(csi2rx->pixel_rst[i]);
-		clk_disable_unprepare(csi2rx->pixel_clk[i]);
 	}
 
-	reset_control_assert(csi2rx->p_rst);
-	clk_disable_unprepare(csi2rx->p_clk);
-
 	if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false))
 		dev_warn(csi2rx->dev, "Couldn't disable our subdev\n");
 
@@ -374,9 +338,15 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
 		 * enable the whole controller.
 		 */
 		if (!csi2rx->count) {
+			ret = pm_runtime_resume_and_get(csi2rx->dev);
+			if (ret < 0)
+				goto out;
+
 			ret = csi2rx_start(csi2rx);
-			if (ret)
+			if (ret) {
+				pm_runtime_put(csi2rx->dev);
 				goto out;
+			}
 		}
 
 		csi2rx->count++;
@@ -386,8 +356,10 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
 		/*
 		 * Let the last user turn off the lights.
 		 */
-		if (!csi2rx->count)
+		if (!csi2rx->count) {
 			csi2rx_stop(csi2rx);
+			pm_runtime_put(csi2rx->dev);
+		}
 	}
 
 out:
@@ -707,6 +679,7 @@ static int csi2rx_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_cleanup;
 
+	pm_runtime_enable(csi2rx->dev);
 	ret = v4l2_async_register_subdev(&csi2rx->subdev);
 	if (ret < 0)
 		goto err_free_state;
@@ -721,6 +694,7 @@ static int csi2rx_probe(struct platform_device *pdev)
 
 err_free_state:
 	v4l2_subdev_cleanup(&csi2rx->subdev);
+	pm_runtime_disable(csi2rx->dev);
 err_cleanup:
 	v4l2_async_nf_unregister(&csi2rx->notifier);
 	v4l2_async_nf_cleanup(&csi2rx->notifier);
@@ -739,9 +713,73 @@ static void csi2rx_remove(struct platform_device *pdev)
 	v4l2_async_unregister_subdev(&csi2rx->subdev);
 	v4l2_subdev_cleanup(&csi2rx->subdev);
 	media_entity_cleanup(&csi2rx->subdev.entity);
+	pm_runtime_disable(csi2rx->dev);
 	kfree(csi2rx);
 }
 
+static int csi2rx_runtime_suspend(struct device *dev)
+{
+	struct csi2rx_priv *csi2rx = dev_get_drvdata(dev);
+	unsigned int i;
+
+	reset_control_assert(csi2rx->sys_rst);
+	clk_disable_unprepare(csi2rx->sys_clk);
+
+	for (i = 0; i < csi2rx->max_streams; i++) {
+		reset_control_assert(csi2rx->pixel_rst[i]);
+		clk_disable_unprepare(csi2rx->pixel_clk[i]);
+	}
+
+	reset_control_assert(csi2rx->p_rst);
+	clk_disable_unprepare(csi2rx->p_clk);
+
+	return 0;
+}
+
+static int csi2rx_runtime_resume(struct device *dev)
+{
+	struct csi2rx_priv *csi2rx = dev_get_drvdata(dev);
+	unsigned int i;
+	int ret;
+
+	ret = clk_prepare_enable(csi2rx->p_clk);
+	if (ret)
+		return ret;
+
+	reset_control_deassert(csi2rx->p_rst);
+
+	for (i = 0; i < csi2rx->max_streams; i++) {
+		ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
+		if (ret)
+			goto err_disable_pixclk;
+
+		reset_control_deassert(csi2rx->pixel_rst[i]);
+	}
+
+	ret = clk_prepare_enable(csi2rx->sys_clk);
+	if (ret)
+		goto err_disable_pixclk;
+
+	reset_control_deassert(csi2rx->sys_rst);
+
+	return 0;
+
+err_disable_pixclk:
+	for (; i > 0; i--) {
+		reset_control_assert(csi2rx->pixel_rst[i - 1]);
+		clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
+	}
+
+	reset_control_assert(csi2rx->p_rst);
+	clk_disable_unprepare(csi2rx->p_clk);
+
+	return ret;
+}
+
+static const struct dev_pm_ops csi2rx_pm_ops = {
+	RUNTIME_PM_OPS(csi2rx_runtime_suspend, csi2rx_runtime_resume, NULL)
+};
+
 static const struct of_device_id csi2rx_of_table[] = {
 	{ .compatible = "starfive,jh7110-csi2rx" },
 	{ .compatible = "cdns,csi2rx" },
@@ -756,6 +794,7 @@ static struct platform_driver csi2rx_driver = {
 	.driver	= {
 		.name		= "cdns-csi2rx",
 		.of_match_table	= csi2rx_of_table,
+		.pm		= &csi2rx_pm_ops,
 	},
 };
 module_platform_driver(csi2rx_driver);

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] media: ti: j721e-csi2rx: Support runtime suspend
  2025-02-24 13:35 [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Jai Luthra
  2025-02-24 13:35 ` [PATCH 1/3] media: cadence: csi2rx: Support runtime PM Jai Luthra
@ 2025-02-24 13:35 ` Jai Luthra
  2025-02-24 13:35 ` [PATCH 3/3] media: ti: j721e-csi2rx: Support system suspend using pm_notifier Jai Luthra
  2025-08-04  7:32 ` [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Rishikesh Donadkar
  3 siblings, 0 replies; 5+ messages in thread
From: Jai Luthra @ 2025-02-24 13:35 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Maxime Ripard, Philipp Zabel, Sakari Ailus
  Cc: linux-media, linux-kernel, Laurent Pinchart, Tomi Valkeinen,
	Changhuang Liang, Devarsh Thakkar, Jai Luthra

Add support for runtime power-management to enable powering off the
shared power domain between Cadence CSI2RX and TI CSI2RX wrapper when
the device(s) are not in use.

When powering off the IP, the PSI-L endpoint loses the paired DMA
channels. Thus we have to release the DMA channels at runtime suspend
and request them again at resume.

Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
 drivers/media/platform/ti/Kconfig                  |  1 +
 .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c  | 71 +++++++++++++++++++---
 2 files changed, 62 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig
index bab998c4179aca3b07372782b9be7de340cb8d45..9d06a537ca9393b6e87a7ab196d26137d4008374 100644
--- a/drivers/media/platform/ti/Kconfig
+++ b/drivers/media/platform/ti/Kconfig
@@ -69,6 +69,7 @@ config VIDEO_TI_J721E_CSI2RX
 	depends on MEDIA_SUPPORT && MEDIA_CONTROLLER
 	depends on (PHY_CADENCE_DPHY_RX && VIDEO_CADENCE_CSI2RX) || COMPILE_TEST
 	depends on ARCH_K3 || COMPILE_TEST
+	depends on PM
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_FWNODE
 	help
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 6412a00be8eab89548950dd21b3b3ec02dafa5b4..dc15759562f94fcfc53955b455b7e03fb733e8e4 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <media/mipi-csi2.h>
 #include <media/v4l2-device.h>
@@ -808,12 +809,16 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
 	unsigned long flags;
 	int ret = 0;
 
+	ret = pm_runtime_resume_and_get(csi->dev);
+	if (ret)
+		return ret;
+
 	spin_lock_irqsave(&dma->lock, flags);
 	if (list_empty(&dma->queue))
 		ret = -EIO;
 	spin_unlock_irqrestore(&dma->lock, flags);
 	if (ret)
-		return ret;
+		goto err;
 
 	ret = video_device_pipeline_start(&csi->vdev, &csi->pipe);
 	if (ret)
@@ -851,6 +856,8 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
 	writel(0, csi->shim + SHIM_DMACNTX);
 err:
 	ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED);
+	pm_runtime_put(csi->dev);
+
 	return ret;
 }
 
@@ -870,6 +877,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
 
 	ti_csi2rx_stop_dma(csi);
 	ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR);
+	pm_runtime_put(csi->dev);
 }
 
 static const struct vb2_ops csi_vb2_qops = {
@@ -963,19 +971,13 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = {
 	.link_validate = ti_csi2rx_link_validate,
 };
 
-static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_dma_request_chan(struct ti_csi2rx_dev *csi)
 {
 	struct dma_slave_config cfg = {
 		.src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES,
 	};
 	int ret;
 
-	INIT_LIST_HEAD(&csi->dma.queue);
-	INIT_LIST_HEAD(&csi->dma.submitted);
-	spin_lock_init(&csi->dma.lock);
-
-	csi->dma.state = TI_CSI2RX_DMA_STOPPED;
-
 	csi->dma.chan = dma_request_chan(csi->dev, "rx0");
 	if (IS_ERR(csi->dma.chan))
 		return PTR_ERR(csi->dma.chan);
@@ -983,9 +985,25 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi)
 	ret = dmaengine_slave_config(csi->dma.chan, &cfg);
 	if (ret) {
 		dma_release_channel(csi->dma.chan);
-		return ret;
 	}
 
+	return ret;
+}
+
+static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi)
+{
+	int ret;
+
+	INIT_LIST_HEAD(&csi->dma.queue);
+	INIT_LIST_HEAD(&csi->dma.submitted);
+	spin_lock_init(&csi->dma.lock);
+
+	csi->dma.state = TI_CSI2RX_DMA_STOPPED;
+
+	ret = ti_csi2rx_dma_request_chan(csi);
+	if (ret)
+		return ret;
+
 	csi->dma.drain.len = DRAIN_BUFFER_SIZE;
 	csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len,
 						  &csi->dma.drain.paddr,
@@ -1062,7 +1080,9 @@ static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi)
 	dma_free_coherent(csi->dev, csi->dma.drain.len,
 			  csi->dma.drain.vaddr, csi->dma.drain.paddr);
 	csi->dma.drain.vaddr = NULL;
-	dma_release_channel(csi->dma.chan);
+
+	if (!pm_runtime_status_suspended(csi->dev))
+		dma_release_channel(csi->dma.chan);
 }
 
 static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
@@ -1083,6 +1103,29 @@ static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi)
 	vb2_queue_release(&csi->vidq);
 }
 
+static int ti_csi2rx_runtime_suspend(struct device *dev)
+{
+	struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+
+	/* DMA channel pairing is lost when device is powered off */
+	dma_release_channel(csi->dma.chan);
+
+	return 0;
+}
+
+static int ti_csi2rx_runtime_resume(struct device *dev)
+{
+	struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+
+	/* Re-acquire DMA channel */
+	return ti_csi2rx_dma_request_chan(csi);
+}
+
+static const struct dev_pm_ops ti_csi2rx_pm_ops = {
+	RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume,
+		       NULL)
+};
+
 static int ti_csi2rx_probe(struct platform_device *pdev)
 {
 	struct ti_csi2rx_dev *csi;
@@ -1124,6 +1167,10 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
 		goto err_subdev;
 	}
 
+	pm_runtime_set_active(csi->dev);
+	pm_runtime_enable(csi->dev);
+	pm_request_idle(csi->dev);
+
 	return 0;
 
 err_subdev:
@@ -1150,6 +1197,9 @@ static void ti_csi2rx_remove(struct platform_device *pdev)
 	ti_csi2rx_cleanup_v4l2(csi);
 	ti_csi2rx_cleanup_dma(csi);
 
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
 	mutex_destroy(&csi->mutex);
 }
 
@@ -1165,6 +1215,7 @@ static struct platform_driver ti_csi2rx_pdrv = {
 	.driver = {
 		.name = TI_CSI2RX_MODULE_NAME,
 		.of_match_table = ti_csi2rx_of_match,
+		.pm		= &ti_csi2rx_pm_ops,
 	},
 };
 

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] media: ti: j721e-csi2rx: Support system suspend using pm_notifier
  2025-02-24 13:35 [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Jai Luthra
  2025-02-24 13:35 ` [PATCH 1/3] media: cadence: csi2rx: Support runtime PM Jai Luthra
  2025-02-24 13:35 ` [PATCH 2/3] media: ti: j721e-csi2rx: Support runtime suspend Jai Luthra
@ 2025-02-24 13:35 ` Jai Luthra
  2025-08-04  7:32 ` [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Rishikesh Donadkar
  3 siblings, 0 replies; 5+ messages in thread
From: Jai Luthra @ 2025-02-24 13:35 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Maxime Ripard, Philipp Zabel, Sakari Ailus
  Cc: linux-media, linux-kernel, Laurent Pinchart, Tomi Valkeinen,
	Changhuang Liang, Devarsh Thakkar, Jai Luthra

As this device is the "orchestrator" for the rest of the media
pipeline, we need to stop all on-going streams before system suspend and
enable them back when the system wakes up from sleep.

Using .suspend/.resume callbacks does not work, as the order of those
callbacks amongst various devices in the camera pipeline like the sensor,
FPD serdes, CSI bridge etc. is impossible to enforce, even with
device links. For example, the Cadence CSI bridge is a child device of
this device, thus we cannot create a device link with the CSI bridge as
a provider and this device as consumer. This can lead to situations
where all the dependencies for the bridge have not yet resumed when we
request the subdev to start streaming again through the .resume callback
defined in this device.

Instead here we register a notifier callback with the PM framework
which is triggered when the system is fully functional. At this point we
can cleanly stop or start the streams, because we know all other devices
and their dependencies are functional. A downside of this approach is
that the userspace is also alive (not frozen yet, or just thawed), so to
prevent races from userspace ioctl calls we hold the lock for the video
device operations while we are dealing with the notifier events.

Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
 .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c  | 109 +++++++++++++++++++++
 1 file changed, 109 insertions(+)

diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index dc15759562f94fcfc53955b455b7e03fb733e8e4..5a318b2080532882b0f0fcbde8e1c35208d198c7 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -107,6 +107,7 @@ struct ti_csi2rx_dev {
 	struct mutex			mutex; /* To serialize ioctls. */
 	struct v4l2_format		v_fmt;
 	struct ti_csi2rx_dma		dma;
+	struct notifier_block		pm_notifier;
 	u32				sequence;
 };
 
@@ -1121,6 +1122,105 @@ static int ti_csi2rx_runtime_resume(struct device *dev)
 	return ti_csi2rx_dma_request_chan(csi);
 }
 
+static int ti_csi2rx_suspend(struct device *dev)
+{
+	struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+	enum ti_csi2rx_dma_state state;
+	struct ti_csi2rx_dma *dma = &csi->dma;
+	unsigned long flags = 0;
+	int ret = 0;
+
+	/* If device was not in use we can simply suspend */
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	/* Acquire the lock used for video dev/queue operations */
+	mutex_lock(&csi->mutex);
+
+	/* Stop any on-going streams */
+	writel(0, csi->shim + SHIM_CNTL);
+	writel(0, csi->shim + SHIM_DMACNTX);
+
+	spin_lock_irqsave(&dma->lock, flags);
+	state = dma->state;
+	spin_unlock_irqrestore(&dma->lock, flags);
+
+	if (state != TI_CSI2RX_DMA_STOPPED) {
+		/* Disable source */
+		ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
+		if (ret)
+			dev_err(csi->dev, "Failed to stop subdev stream\n");
+	}
+
+	/* Drain DMA */
+	ti_csi2rx_drain_dma(csi);
+
+	/* Terminate DMA */
+	ret = dmaengine_terminate_sync(csi->dma.chan);
+	if (ret)
+		dev_err(csi->dev, "Failed to stop DMA\n");
+
+	return ret;
+}
+
+static int ti_csi2rx_resume(struct device *dev)
+{
+	struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+	struct ti_csi2rx_dma *dma = &csi->dma;
+	struct ti_csi2rx_buffer *buf;
+	unsigned long flags = 0;
+	int ret = 0;
+
+	/* If device was not in use, we can simply wakeup */
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	spin_lock_irqsave(&dma->lock, flags);
+	if (dma->state != TI_CSI2RX_DMA_STOPPED) {
+		/* Re-submit all previously submitted buffers to DMA */
+		list_for_each_entry(buf, &csi->dma.submitted, list) {
+			ti_csi2rx_start_dma(csi, buf);
+		}
+		spin_unlock_irqrestore(&dma->lock, flags);
+
+		/* Restore stream config */
+		ti_csi2rx_setup_shim(csi);
+
+		ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
+		if (ret)
+			dev_err(csi->dev, "Failed to start subdev\n");
+	} else {
+		spin_unlock_irqrestore(&dma->lock, flags);
+	}
+
+	/* Release the lock used for video dev/queue operations */
+	mutex_unlock(&csi->mutex);
+
+	return ret;
+}
+
+static int ti_csi2rx_pm_notifier(struct notifier_block *nb,
+				 unsigned long action, void *data)
+{
+	struct ti_csi2rx_dev *csi =
+		container_of(nb, struct ti_csi2rx_dev, pm_notifier);
+
+	switch (action) {
+	case PM_HIBERNATION_PREPARE:
+	case PM_SUSPEND_PREPARE:
+	case PM_RESTORE_PREPARE:
+		ti_csi2rx_suspend(csi->dev);
+		break;
+	case PM_POST_SUSPEND:
+	case PM_POST_HIBERNATION:
+	case PM_POST_RESTORE:
+		ti_csi2rx_resume(csi->dev);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
 static const struct dev_pm_ops ti_csi2rx_pm_ops = {
 	RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume,
 		       NULL)
@@ -1167,6 +1267,13 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
 		goto err_subdev;
 	}
 
+	csi->pm_notifier.notifier_call = ti_csi2rx_pm_notifier;
+	ret = register_pm_notifier(&csi->pm_notifier);
+	if (ret) {
+		dev_err(csi->dev, "Failed to create PM notifier: %d\n", ret);
+		goto err_subdev;
+	}
+
 	pm_runtime_set_active(csi->dev);
 	pm_runtime_enable(csi->dev);
 	pm_request_idle(csi->dev);
@@ -1192,6 +1299,8 @@ static void ti_csi2rx_remove(struct platform_device *pdev)
 
 	video_unregister_device(&csi->vdev);
 
+	unregister_pm_notifier(&csi->pm_notifier);
+
 	ti_csi2rx_cleanup_vb2q(csi);
 	ti_csi2rx_cleanup_subdev(csi);
 	ti_csi2rx_cleanup_v4l2(csi);

-- 
2.48.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX
  2025-02-24 13:35 [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Jai Luthra
                   ` (2 preceding siblings ...)
  2025-02-24 13:35 ` [PATCH 3/3] media: ti: j721e-csi2rx: Support system suspend using pm_notifier Jai Luthra
@ 2025-08-04  7:32 ` Rishikesh Donadkar
  3 siblings, 0 replies; 5+ messages in thread
From: Rishikesh Donadkar @ 2025-08-04  7:32 UTC (permalink / raw)
  To: Jai Luthra, Mauro Carvalho Chehab, Maxime Ripard, Philipp Zabel,
	Sakari Ailus
  Cc: linux-media, linux-kernel, Laurent Pinchart, Tomi Valkeinen,
	Changhuang Liang, Devarsh Thakkar


On 24/02/25 19:05, Jai Luthra wrote:
> This series adds support for runtime power management and system sleep
> for the TI (J721E) CSI2RX pipeline.
>
> Some basic testing with suspend-to-idle mode has been performed to
> ensure that any ongoing stream resumes smoothly after wakeup. More
> testing with suspend-to-memory mode will be done later, as I need to
> update the device management boot firmware, which supports newer APIs
> used by the mainline kernel.
>
> I have picked PATCH 1/3 from Changhuang's earlier series [1] with minor
> modifications.
>
> Link: https://lore.kernel.org/linux-media/20240718032834.53876-1-changhuang.liang@starfivetech.com/ [1]
>
> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>


Hi Jai,

Thank you for the patches. For the entire series,

Tested-by: Rishikesh Donadkar <r-donadkar@ti.com>

Reviewed-by: Rishikesh Donadkar <r-donadkar@ti.com>


Test Logs (AM62A): 
https://gist.github.com/Rishikesh-D/303218d56f51c8acdc02282fef40da36


> ---
> Changhuang Liang (1):
>        media: cadence: csi2rx: Support runtime PM
>
> Jai Luthra (2):
>        media: ti: j721e-csi2rx: Support runtime suspend
>        media: ti: j721e-csi2rx: Support system suspend using pm_notifier
>
>   drivers/media/platform/cadence/Kconfig             |   1 +
>   drivers/media/platform/cadence/cdns-csi2rx.c       | 121 +++++++++-----
>   drivers/media/platform/ti/Kconfig                  |   1 +
>   .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c  | 180 +++++++++++++++++++--
>   4 files changed, 252 insertions(+), 51 deletions(-)
> ---
> base-commit: d082ecbc71e9e0bf49883ee4afd435a77a5101b6
> change-id: 20250224-ti_csi_pm-24808b2b445c
>
> Best regards,

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-08-04  7:32 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-24 13:35 [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Jai Luthra
2025-02-24 13:35 ` [PATCH 1/3] media: cadence: csi2rx: Support runtime PM Jai Luthra
2025-02-24 13:35 ` [PATCH 2/3] media: ti: j721e-csi2rx: Support runtime suspend Jai Luthra
2025-02-24 13:35 ` [PATCH 3/3] media: ti: j721e-csi2rx: Support system suspend using pm_notifier Jai Luthra
2025-08-04  7:32 ` [PATCH 0/3] media: Support runtime PM and system sleep for TI-CSI2RX Rishikesh Donadkar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).