/* * 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; }
/* * ipipeif_isif0_isr - Handle ISIF0 event * @ipipeif: Pointer to ISP IPIPEIF device. * * Executes LSC deferred enablement before next frame starts. */ static void ipipeif_isif0_isr(struct iss_ipipeif_device *ipipeif) { struct iss_pipeline *pipe = to_iss_pipeline(&ipipeif->subdev.entity); if (pipe->do_propagation) atomic_inc(&pipe->frame_number); if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) ipipeif_isr_buffer(ipipeif); }
/* * iss_pipeline_is_last - Verify if entity has an enabled link to the output * video node * @me: ISS module's media entity * * Returns 1 if the entity has an enabled link to the output video node or 0 * otherwise. It's true only while pipeline can have no more than one output * node. */ static int iss_pipeline_is_last(struct media_entity *me) { struct iss_pipeline *pipe; struct media_pad *pad; if (!me->pipe) return 0; pipe = to_iss_pipeline(me); if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED) return 0; pad = media_entity_remote_pad(&pipe->output->pad); return pad->entity == me; }
static void iss_video_buf_queue(struct vb2_buffer *vb) { struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue); struct iss_video *video = vfh->video; struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb); struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); unsigned long flags; bool empty; spin_lock_irqsave(&video->qlock, flags); /* Mark the buffer is faulty and give it back to the queue immediately * if the video node has registered an error. vb2 will perform the same * check when preparing the buffer, but that is inherently racy, so we * need to handle the race condition with an authoritative check here. */ if (unlikely(video->error)) { vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&video->qlock, flags); return; } empty = list_empty(&video->dmaqueue); list_add_tail(&buffer->list, &video->dmaqueue); spin_unlock_irqrestore(&video->qlock, flags); if (empty) { enum iss_pipeline_state state; unsigned int start; if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISS_PIPELINE_QUEUE_OUTPUT; else state = ISS_PIPELINE_QUEUE_INPUT; spin_lock_irqsave(&pipe->lock, flags); pipe->state |= state; video->ops->queue(video, buffer); video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED; start = iss_pipeline_ready(pipe); if (start) pipe->state |= ISS_PIPELINE_STREAM; spin_unlock_irqrestore(&pipe->lock, flags); if (start) omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_SINGLESHOT); } }
/* * omap4iss_csi2_isr - CSI2 interrupt handling. */ void omap4iss_csi2_isr(struct iss_csi2_device *csi2) { struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); u32 csi2_irqstatus, cpxio1_irqstatus; struct iss_device *iss = csi2->iss; if (!csi2->available) return; csi2_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, CSI2_IRQSTATUS); iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQSTATUS, csi2_irqstatus); /* Failure Cases */ if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) { cpxio1_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS); iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS, cpxio1_irqstatus); dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ %x\n", cpxio1_irqstatus); pipe->error = true; } if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR | CSI2_IRQ_SHORT_PACKET | CSI2_IRQ_ECC_NO_CORRECTION | CSI2_IRQ_COMPLEXIO_ERR | CSI2_IRQ_FIFO_OVF)) { dev_dbg(iss->dev, "CSI2 Err: OCP:%d SHORT:%d ECC:%d CPXIO:%d OVF:%d\n", csi2_irqstatus & CSI2_IRQ_OCP_ERR ? 1 : 0, csi2_irqstatus & CSI2_IRQ_SHORT_PACKET ? 1 : 0, csi2_irqstatus & CSI2_IRQ_ECC_NO_CORRECTION ? 1 : 0, csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR ? 1 : 0, csi2_irqstatus & CSI2_IRQ_FIFO_OVF ? 1 : 0); pipe->error = true; } if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) return; /* Successful cases */ if (csi2_irqstatus & CSI2_IRQ_CONTEXT0) csi2_isr_ctx(csi2, &csi2->contexts[0]); if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION) dev_dbg(iss->dev, "CSI2: ECC correction done\n"); }
/* * iss_module_sync_idle - Helper to sync module with its idle state * @me: ISS submodule's media entity * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization * @stopping: flag which tells module wants to stop * * This function checks if ISS submodule needs to wait for next interrupt. If * yes, makes the caller to sleep while waiting for such event. */ int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, atomic_t *stopping) { struct iss_pipeline *pipe = to_iss_pipeline(me); struct iss_video *video = pipe->output; unsigned long flags; if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED || (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT && !iss_pipeline_ready(pipe))) return 0; /* * atomic_set() doesn't include memory barrier on ARM platform for SMP * scenario. We'll call it here to avoid race conditions. */ atomic_set(stopping, 1); smp_wmb(); /* * If module is the last one, it's writing to memory. In this case, * it's necessary to check if the module is already paused due to * DMA queue underrun or if it has to wait for next interrupt to be * idle. * If it isn't the last one, the function won't sleep but *stopping * will still be set to warn next submodule caller's interrupt the * module wants to be idle. */ if (!iss_pipeline_is_last(me)) return 0; spin_lock_irqsave(&video->qlock, flags); if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) { spin_unlock_irqrestore(&video->qlock, flags); atomic_set(stopping, 0); smp_wmb(); return 0; } spin_unlock_irqrestore(&video->qlock, flags); if (!wait_event_timeout(*wait, !atomic_read(stopping), msecs_to_jiffies(1000))) { atomic_set(stopping, 0); smp_wmb(); return -ETIMEDOUT; } return 0; }
static int csi2_link_validate(struct v4l2_subdev *sd, struct media_link *link, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); int rval; pipe->external = media_entity_to_v4l2_subdev(link->source->entity); rval = omap4iss_get_external_info(pipe, link); if (rval < 0) return rval; return v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); }
static void iss_video_buf_queue(struct vb2_buffer *vb) { struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue); struct iss_video *video = vfh->video; struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb); struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); unsigned long flags; bool empty; spin_lock_irqsave(&video->qlock, flags); if (unlikely(video->error)) { vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&video->qlock, flags); return; } empty = list_empty(&video->dmaqueue); list_add_tail(&buffer->list, &video->dmaqueue); spin_unlock_irqrestore(&video->qlock, flags); if (empty) { enum iss_pipeline_state state; unsigned int start; if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISS_PIPELINE_QUEUE_OUTPUT; else state = ISS_PIPELINE_QUEUE_INPUT; spin_lock_irqsave(&pipe->lock, flags); pipe->state |= state; video->ops->queue(video, buffer); video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED; start = iss_pipeline_ready(pipe); if (start) pipe->state |= ISS_PIPELINE_STREAM; spin_unlock_irqrestore(&pipe->lock, flags); if (start) omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_SINGLESHOT); } }
static void csi2_isr_ctx(struct iss_csi2_device *csi2, struct iss_csi2_ctx_cfg *ctx) { unsigned int n = ctx->ctxnum; u32 status; status = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n)); iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n), status); /* Propagate frame number */ if (status & CSI2_CTX_IRQ_FS) { struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); if (pipe->do_propagation) atomic_inc(&pipe->frame_number); } if (!(status & CSI2_CTX_IRQ_FE)) 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(csi2, ctx); csi2_ctx_enable(csi2, n, 1); } return; } if (csi2->output & CSI2_OUTPUT_MEMORY) csi2_isr_buffer(csi2); }
static int iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); enum iss_pipeline_state state; unsigned long flags; if (type != video->type) return -EINVAL; mutex_lock(&video->stream_lock); if (!vb2_is_streaming(&vfh->queue)) goto done; /* Update the pipeline state. */ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_QUEUE_OUTPUT; else state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_QUEUE_INPUT; spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~state; spin_unlock_irqrestore(&pipe->lock, flags); /* Stop the stream. */ omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED); vb2_streamoff(&vfh->queue, type); video->queue = NULL; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); media_entity_pipeline_stop(&video->video.entity); done: mutex_unlock(&video->stream_lock); return 0; }
/* * Stream management * * Every ISS pipeline has a single input and a single output. The input can be * either a sensor or a video node. The output is always a video node. * * As every pipeline has an output video node, the ISS video objects at the * pipeline output stores the pipeline state. It tracks the streaming state of * both the input and output, as well as the availability of buffers. * * In sensor-to-memory mode, frames are always available at the pipeline input. * Starting the sensor usually requires I2C transfers and must be done in * interruptible context. The pipeline is started and stopped synchronously * to the stream on/off commands. All modules in the pipeline will get their * subdev set stream handler called. The module at the end of the pipeline must * delay starting the hardware until buffers are available at its output. * * In memory-to-memory mode, starting/stopping the stream requires * synchronization between the input and output. ISS modules can't be stopped * in the middle of a frame, and at least some of the modules seem to become * busy as soon as they're started, even if they don't receive a frame start * event. For that reason frames need to be processed in single-shot mode. The * driver needs to wait until a frame is completely processed and written to * memory before restarting the pipeline for the next frame. Pipelined * processing might be possible but requires more testing. * * Stream start must be delayed until buffers are available at both the input * and output. The pipeline must be started in the videobuf queue callback with * the buffers queue spinlock held. The modules subdev set stream operation must * not sleep. */ static int iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); struct media_entity_graph graph; struct media_entity *entity; enum iss_pipeline_state state; struct iss_pipeline *pipe; struct iss_video *far_end; unsigned long flags; int ret; if (type != video->type) return -EINVAL; mutex_lock(&video->stream_lock); /* Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ pipe = video->video.entity.pipe ? to_iss_pipeline(&video->video.entity) : &video->pipe; pipe->external = NULL; pipe->external_rate = 0; pipe->external_bpp = 0; pipe->entities = 0; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, true); ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); if (ret < 0) goto err_media_entity_pipeline_start; entity = &video->video.entity; media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) pipe->entities |= 1 << entity->id; /* Verify that the currently configured format matches the output of * the connected subdev. */ ret = iss_video_check_format(video, vfh); if (ret < 0) goto err_iss_video_check_format; video->bpl_padding = ret; video->bpl_value = vfh->format.fmt.pix.bytesperline; /* Find the ISS video node connected at the far end of the pipeline and * update the pipeline. */ far_end = iss_video_far_end(video); if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT; pipe->input = far_end; pipe->output = video; } else { if (far_end == NULL) { ret = -EPIPE; goto err_iss_video_check_format; } state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT; pipe->input = video; pipe->output = far_end; } spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~ISS_PIPELINE_STREAM; pipe->state |= state; spin_unlock_irqrestore(&pipe->lock, flags); /* Set the maximum time per frame as the value requested by userspace. * This is a soft limit that can be overridden if the hardware doesn't * support the request limit. */ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) pipe->max_timeperframe = vfh->timeperframe; video->queue = &vfh->queue; INIT_LIST_HEAD(&video->dmaqueue); spin_lock_init(&video->qlock); video->error = false; atomic_set(&pipe->frame_number, -1); ret = vb2_streamon(&vfh->queue, type); if (ret < 0) goto err_iss_video_check_format; /* In sensor-to-memory mode, the stream can be started synchronously * to the stream on command. In memory-to-memory mode, it will be * started when buffers are queued on both the input and output. */ if (pipe->input == NULL) { unsigned long flags; ret = omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_CONTINUOUS); if (ret < 0) goto err_omap4iss_set_stream; spin_lock_irqsave(&video->qlock, flags); if (list_empty(&video->dmaqueue)) video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN; spin_unlock_irqrestore(&video->qlock, flags); } mutex_unlock(&video->stream_lock); return 0; err_omap4iss_set_stream: vb2_streamoff(&vfh->queue, type); err_iss_video_check_format: media_entity_pipeline_stop(&video->video.entity); err_media_entity_pipeline_start: if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); video->queue = NULL; mutex_unlock(&video->stream_lock); return ret; }
/* * omap4iss_video_buffer_next - Complete the current buffer and return the next * @video: ISS video object * * Remove the current video buffer from the DMA queue and fill its timestamp, * field count and state fields before waking up its completion handler. * * For capture video nodes, the buffer state is set to VB2_BUF_STATE_DONE if no * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. * * The DMA queue is expected to contain at least one buffer. * * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is * empty. */ struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video) { struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); enum iss_pipeline_state state; struct iss_buffer *buf; unsigned long flags; struct timespec ts; spin_lock_irqsave(&video->qlock, flags); if (WARN_ON(list_empty(&video->dmaqueue))) { spin_unlock_irqrestore(&video->qlock, flags); return NULL; } buf = list_first_entry(&video->dmaqueue, struct iss_buffer, list); list_del(&buf->list); spin_unlock_irqrestore(&video->qlock, flags); ktime_get_ts(&ts); buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec; buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; /* Do frame number propagation only if this is the output video node. * Frame number either comes from the CSI receivers or it gets * incremented here if H3A is not active. * Note: There is no guarantee that the output buffer will finish * first, so the input number might lag behind by 1 in some cases. */ if (video == pipe->output && !pipe->do_propagation) buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number); else buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number); vb2_buffer_done(&buf->vb, pipe->error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); pipe->error = false; spin_lock_irqsave(&video->qlock, flags); if (list_empty(&video->dmaqueue)) { spin_unlock_irqrestore(&video->qlock, flags); if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISS_PIPELINE_QUEUE_OUTPUT | ISS_PIPELINE_STREAM; else state = ISS_PIPELINE_QUEUE_INPUT | ISS_PIPELINE_STREAM; spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~state; if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS) video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN; spin_unlock_irqrestore(&pipe->lock, flags); return NULL; } if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { spin_lock(&pipe->lock); pipe->state &= ~ISS_PIPELINE_STREAM; spin_unlock(&pipe->lock); } buf = list_first_entry(&video->dmaqueue, struct iss_buffer, list); spin_unlock_irqrestore(&video->qlock, flags); buf->vb.state = VB2_BUF_STATE_ACTIVE; return buf; }