summaryrefslogtreecommitdiff
path: root/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c')
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c130
1 files changed, 113 insertions, 17 deletions
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index e44a4d1dd1af..092805df1e3f 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/of_platform.h>
+#include <linux/pm.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -773,6 +774,31 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx,
return 0;
}
+static int ti_csi2rx_restart_dma(struct ti_csi2rx_ctx *ctx,
+ struct ti_csi2rx_buffer *buf)
+{
+ struct ti_csi2rx_dma *dma = &ctx->dma;
+ unsigned long flags = 0;
+ int ret = 0;
+
+ ret = ti_csi2rx_drain_dma(ctx);
+ if (ret)
+ dev_warn(ctx->csi->dev,
+ "Failed to drain DMA. Next frame might be bogus\n");
+
+ ret = ti_csi2rx_start_dma(ctx, buf);
+ if (ret) {
+ dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret);
+ spin_lock_irqsave(&dma->lock, flags);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ dma->curr = NULL;
+ dma->state = TI_CSI2RX_DMA_IDLE;
+ spin_unlock_irqrestore(&dma->lock, flags);
+ }
+
+ return ret;
+}
+
static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[],
struct device *alloc_devs[])
@@ -813,7 +839,6 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
struct ti_csi2rx_dma *dma = &ctx->dma;
bool restart_dma = false;
unsigned long flags = 0;
- int ret;
buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf);
buf->ctx = ctx;
@@ -846,20 +871,7 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
* the application and will only confuse it. Issue a DMA
* transaction to drain that up.
*/
- ret = ti_csi2rx_drain_dma(ctx);
- if (ret)
- dev_warn(ctx->csi->dev,
- "Failed to drain DMA. Next frame might be bogus\n");
-
- ret = ti_csi2rx_start_dma(ctx, buf);
- if (ret) {
- dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret);
- spin_lock_irqsave(&dma->lock, flags);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- dma->curr = NULL;
- dma->state = TI_CSI2RX_DMA_IDLE;
- spin_unlock_irqrestore(&dma->lock, flags);
- }
+ ti_csi2rx_restart_dma(ctx, buf);
}
}
@@ -1405,6 +1417,87 @@ cleanup_dma:
return ret;
}
+#ifdef CONFIG_PM
+static int ti_csi2rx_suspend(struct device *dev)
+{
+ struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+ struct ti_csi2rx_ctx *ctx;
+ struct ti_csi2rx_dma *dma;
+ unsigned long flags = 0;
+ int i, ret = 0;
+
+ for (i = 0; i < csi->num_ctx; i++) {
+ ctx = &csi->ctx[i];
+ dma = &ctx->dma;
+
+ spin_lock_irqsave(&dma->lock, flags);
+ if (dma->state != TI_CSI2RX_DMA_STOPPED) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 0);
+ if (ret)
+ dev_err(csi->dev, "Failed to stop subdev stream\n");
+ /* Terminate DMA */
+ ret = dmaengine_terminate_sync(ctx->dma.chan);
+ if (ret)
+ dev_err(csi->dev, "Failed to stop DMA\n");
+ } else {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ }
+
+ /* Stop any on-going streams */
+ writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
+ }
+
+ /* Assert the pixel reset. */
+ writel(0, csi->shim + SHIM_CNTL);
+
+ return ret;
+}
+
+static int ti_csi2rx_resume(struct device *dev)
+{
+ struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+ struct ti_csi2rx_ctx *ctx;
+ struct ti_csi2rx_dma *dma;
+ struct ti_csi2rx_buffer *buf;
+ unsigned long flags = 0;
+ unsigned int reg;
+ int i, ret = 0;
+
+ reg = SHIM_CNTL_PIX_RST;
+ writel(reg, csi->shim + SHIM_CNTL);
+
+ for (i = 0; i < csi->num_ctx; i++) {
+ ctx = &csi->ctx[i];
+ dma = &ctx->dma;
+ spin_lock_irqsave(&dma->lock, flags);
+ if (dma->state != TI_CSI2RX_DMA_STOPPED) {
+ buf = dma->curr;
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ /* Restore stream config */
+ ti_csi2rx_setup_shim(ctx);
+
+ ret = v4l2_subdev_call(&csi->subdev, video, s_stream, 1);
+ if (ret)
+ dev_err(ctx->csi->dev, "Failed to start subdev\n");
+
+ /* Restart DMA */
+ if (buf)
+ ti_csi2rx_restart_dma(ctx, buf);
+ } else {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ }
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops ti_csi2rx_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ti_csi2rx_suspend, ti_csi2rx_resume)
+};
+#endif /* CONFIG_PM */
+
static int ti_csi2rx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -1517,8 +1610,11 @@ static struct platform_driver ti_csi2rx_pdrv = {
.probe = ti_csi2rx_probe,
.remove = ti_csi2rx_remove,
.driver = {
- .name = TI_CSI2RX_MODULE_NAME,
- .of_match_table = ti_csi2rx_of_match,
+ .name = TI_CSI2RX_MODULE_NAME,
+ .of_match_table = ti_csi2rx_of_match,
+#ifdef CONFIG_PM
+ .pm = &ti_csi2rx_pm_ops,
+#endif
},
};