/* * csi2_set_stream - Enable/Disable streaming on the CSI2 module * @sd: ISS CSI2 V4L2 subdevice * @enable: ISS pipeline stream state * * Return 0 on success or a negative error code otherwise. */ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct iss_device *iss = csi2->iss; struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); struct iss_video *video_out = &csi2->video_out; if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) { if (enable == ISS_PIPELINE_STREAM_STOPPED) return 0; omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A); } switch (enable) { case ISS_PIPELINE_STREAM_CONTINUOUS: if (omap4iss_csiphy_acquire(csi2->phy) < 0) return -ENODEV; csi2->use_fs_irq = pipe->do_propagation; csi2_configure(csi2); csi2_print_status(csi2); /* * When outputting to memory with no buffer available, let the * buffer queue handler start the hardware. A DMA queue flag * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is * a buffer available. */ if (csi2->output & CSI2_OUTPUT_MEMORY && !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED)) break; /* Enable context 0 and IRQs */ atomic_set(&csi2->stopping, 0); csi2_ctx_enable(csi2, 0, 1); csi2_if_enable(csi2, 1); iss_video_dmaqueue_flags_clr(video_out); break; case ISS_PIPELINE_STREAM_STOPPED: if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) return 0; if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait, &csi2->stopping)) dev_dbg(iss->dev, "%s: module stop timeout.\n", sd->name); csi2_ctx_enable(csi2, 0, 0); csi2_if_enable(csi2, 0); csi2_irq_ctx_set(csi2, 0); omap4iss_csiphy_release(csi2->phy); omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A); iss_video_dmaqueue_flags_clr(video_out); break; } csi2->state = enable; return 0; }
/* * csi2_set_stream - Enable/Disable streaming on the CSI2 module * @sd: ISP CSI2 V4L2 subdevice * @enable: ISP pipeline stream state * * Return 0 on success or a negative error code otherwise. */ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct isp_device *isp = csi2->isp; struct isp_video *video_out = &csi2->video_out; switch (enable) { case ISP_PIPELINE_STREAM_CONTINUOUS: if (omap3isp_csiphy_acquire(csi2->phy) < 0) return -ENODEV; if (csi2->output & CSI2_OUTPUT_MEMORY) omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); csi2_configure(csi2); csi2_print_status(csi2); /* * When outputting to memory with no buffer available, let the * buffer queue handler start the hardware. A DMA queue flag * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is * a buffer available. */ if (csi2->output & CSI2_OUTPUT_MEMORY && !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED)) break; /* Enable context 0 and IRQs */ atomic_set(&csi2->stopping, 0); csi2_ctx_enable(isp, csi2, 0, 1); csi2_if_enable(isp, csi2, 1); isp_video_dmaqueue_flags_clr(video_out); break; case ISP_PIPELINE_STREAM_STOPPED: if (csi2->state == ISP_PIPELINE_STREAM_STOPPED) return 0; if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait, &csi2->stopping)) dev_dbg(isp->dev, "%s: module stop timeout.\n", sd->name); csi2_ctx_enable(isp, csi2, 0, 0); csi2_if_enable(isp, csi2, 0); csi2_irq_ctx_set(isp, csi2, 0); omap3isp_csiphy_release(csi2->phy); isp_video_dmaqueue_flags_clr(video_out); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); break; } csi2->state = enable; return 0; }
/* * csi2_isr_buffer - Does buffer handling at end-of-frame * when writing to memory. */ static void csi2_isr_buffer(struct iss_csi2_device *csi2) { struct iss_buffer *buffer; csi2_ctx_enable(csi2, 0, 0); buffer = omap4iss_video_buffer_next(&csi2->video_out); /* * Let video queue operation restart engine if there is an underrun * condition. */ if (buffer == NULL) return; csi2_set_outaddr(csi2, buffer->iss_addr); csi2_ctx_enable(csi2, 0, 1); }
static void csi2_isr_ctx(struct isp_csi2_device *csi2, struct isp_csi2_ctx_cfg *ctx) { struct isp_device *isp = csi2->isp; unsigned int n = ctx->ctxnum; u32 status; status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); /* Propagate frame number */ if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); if (pipe->do_propagation) atomic_inc(&pipe->frame_number); } if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) return; /* Skip interrupts until we reach the frame skip count. The CSI2 will be * automatically disabled, as the frame skip count has been programmed * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. * * It would have been nice to rely on the FRAME_NUMBER interrupt instead * but it turned out that the interrupt is only generated when the CSI2 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased * correctly and reaches 0 when data is forwarded to the video port only * but no interrupt arrives). Maybe a CSI2 hardware bug. */ if (csi2->frame_skip) { csi2->frame_skip--; if (csi2->frame_skip == 0) { ctx->format_id = csi2_ctx_map_format(csi2); csi2_ctx_config(isp, csi2, ctx); csi2_ctx_enable(isp, csi2, n, 1); } return; } if (csi2->output & CSI2_OUTPUT_MEMORY) csi2_isr_buffer(csi2); }
/* * csi2_queue - Queues the first buffer when using memory output * @video: The video node * @buffer: buffer to queue */ static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer) { struct isp_device *isp = video->isp; struct isp_csi2_device *csi2 = &isp->isp_csi2a; csi2_set_outaddr(csi2, buffer->dma); /* * If streaming was enabled before there was a buffer queued * or underrun happened in the ISR, the hardware was not enabled * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set. * Enable it now. */ if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { /* Enable / disable context 0 and IRQs */ csi2_if_enable(isp, csi2, 1); csi2_ctx_enable(isp, csi2, 0, 1); isp_video_dmaqueue_flags_clr(&csi2->video_out); } return 0; }